Merge "Set the correct platform type in AudioSystem#getPlatformType()." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 705a4df..c231b30 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -57,7 +57,7 @@
":android.app.flags-aconfig-java{.generated_srcjars}",
":android.credentials.flags-aconfig-java{.generated_srcjars}",
":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
- ":com.android.server.flags.pinner-aconfig-java{.generated_srcjars}",
+ ":com.android.server.flags.services-aconfig-java{.generated_srcjars}",
":android.service.controls.flags-aconfig-java{.generated_srcjars}",
":android.service.voice.flags-aconfig-java{.generated_srcjars}",
":android.media.tv.flags-aconfig-java{.generated_srcjars}",
@@ -430,10 +430,7 @@
aconfig_declarations {
name: "com.android.media.flags.bettertogether-aconfig",
package: "com.android.media.flags",
- srcs: [
- "media/java/android/media/flags/media_better_together.aconfig",
- "media/java/android/media/flags/fade_manager_configuration.aconfig",
- ],
+ srcs: ["media/java/android/media/flags/media_better_together.aconfig"],
}
java_aconfig_library {
@@ -591,16 +588,16 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
-// Pinner Service
+// Server Services Flags
aconfig_declarations {
- name: "com.android.server.flags.pinner-aconfig",
+ name: "com.android.server.flags.services-aconfig",
package: "com.android.server.flags",
- srcs: ["services/core/java/com/android/server/flags/pinner.aconfig"],
+ srcs: ["services/core/java/com/android/server/flags/*.aconfig"],
}
java_aconfig_library {
- name: "com.android.server.flags.pinner-aconfig-java",
- aconfig_declarations: "com.android.server.flags.pinner-aconfig",
+ name: "com.android.server.flags.services-aconfig-java",
+ aconfig_declarations: "com.android.server.flags.services-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
diff --git a/Android.bp b/Android.bp
index bb93048..13b1703 100644
--- a/Android.bp
+++ b/Android.bp
@@ -174,6 +174,9 @@
// and remove this line.
"//frameworks/base/tools/hoststubgen:__subpackages__",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
// AIDL files under these paths are mixture of public and private ones.
@@ -264,6 +267,9 @@
],
sdk_version: "core_platform",
installable: false,
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
// NOTE: This filegroup is exposed for vendor libraries to depend on and is referenced in
@@ -432,6 +438,9 @@
],
sdk_version: "core_platform",
installable: false,
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
// Separated so framework-minus-apex-defaults can be used without the libs dependency
@@ -475,6 +484,9 @@
],
compile_dex: false,
headers_only: true,
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_library {
@@ -502,6 +514,9 @@
"-Xep:AndroidFrameworkUid:ERROR",
],
},
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_library {
@@ -516,6 +531,7 @@
},
lint: {
enabled: false,
+ baseline_filename: "lint-baseline.xml",
},
}
@@ -540,6 +556,9 @@
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_library {
@@ -555,6 +574,9 @@
"calendar-provider-compat-config",
"contacts-provider-platform-compat-config",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
platform_compat_config {
@@ -609,6 +631,9 @@
"rappor",
],
dxflags: ["--core-library"],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
// utility classes statically linked into framework-wifi and dynamically linked
@@ -634,6 +659,9 @@
"//frameworks/base/services/net",
"//packages/modules/Wifi/framework",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
filegroup {
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 03f3f0f..f330ad1 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -65,7 +65,7 @@
// depend on it.
java_genrule {
name: "framework-minus-apex.ravenwood",
- defaults: ["hoststubgen-for-prototype-only-genrule"],
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-minus-apex.ravenwood-base{ravenwood.jar}",
diff --git a/SECURITY_STATE_OWNERS b/SECURITY_STATE_OWNERS
new file mode 100644
index 0000000..30ddfe2
--- /dev/null
+++ b/SECURITY_STATE_OWNERS
@@ -0,0 +1,5 @@
+alxu@google.com
+musashi@google.com
+maunik@google.com
+davidkwak@google.com
+willcoster@google.com
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index e5a06c9..3c361d7 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -16,12 +16,14 @@
package android.graphics.perftests;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
+import android.graphics.ColorSpace;
import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
@@ -104,15 +106,36 @@
}
@Test
- public void testCreateScaledBitmap() throws IOException {
+ public void testCreateScaledSrgbBitmap() throws IOException {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final Context context = InstrumentationRegistry.getContext();
Bitmap source = ImageDecoder.decodeBitmap(
ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
(decoder, info, source1) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
});
source.setGainmap(null);
+ assertEquals(source.getColorSpace().getId(), ColorSpace.Named.SRGB.ordinal());
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
+
+ @Test
+ public void testCreateScaledP3Bitmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+ });
+ source.setGainmap(null);
+ assertEquals(source.getColorSpace().getId(), ColorSpace.Named.DISPLAY_P3.ordinal());
while (state.keepRunning()) {
Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
diff --git a/apct-tests/perftests/healthconnect/Android.bp b/apct-tests/perftests/healthconnect/Android.bp
new file mode 100644
index 0000000..c2d0a6f
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/Android.bp
@@ -0,0 +1,44 @@
+// 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 {
+ // 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: "HealthConnectPerfTests",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "apct-perftests-utils",
+ "collector-device-lib-platform",
+ ],
+
+ libs: ["android.test.base"],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ data: [":perfetto_artifacts"],
+ certificate: "platform",
+}
diff --git a/apct-tests/perftests/healthconnect/AndroidManifest.xml b/apct-tests/perftests/healthconnect/AndroidManifest.xml
new file mode 100644
index 0000000..6a6370b
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.healthconnect">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.perftests.healthconnect"/>
+</manifest>
diff --git a/apct-tests/perftests/healthconnect/AndroidTest.xml b/apct-tests/perftests/healthconnect/AndroidTest.xml
new file mode 100644
index 0000000..5036202
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/AndroidTest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs HealthConnectPerfTests metric instrumentation.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-metric-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="HealthConnectPerfTests.apk" />
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <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" />
+ </target_preparer>
+
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ </metrics_collector>
+
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.perftests.healthconnect" />
+ <option name="hidden-api-checks" value="false"/>
+
+ <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+ <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+ <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+ <!-- ProcLoadListener related arguments -->
+ <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+ <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+ <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+ <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+ <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+ </test>
+</configuration>
diff --git a/apct-tests/perftests/healthconnect/OWNERS b/apct-tests/perftests/healthconnect/OWNERS
new file mode 100644
index 0000000..da0b46a
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 1219472
+
+arkivanov@google.com
+jstembridge@google.com
+pratyushmore@google.com
+itsleo@google.com
diff --git a/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt b/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt
new file mode 100644
index 0000000..21a4ca0
--- /dev/null
+++ b/apct-tests/perftests/healthconnect/src/com/android/perftests/healthconnect/HealthConnectReadWritePerfTest.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.perftests.healthconnect
+
+import android.health.connect.HealthConnectManager
+import android.os.SystemClock
+import android.perftests.utils.PerfStatusReporter
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Read/write benchmark tests for [HealthConnectManager]
+ *
+ * Build/Install/Run: atest HealthConnectReadWritePerfTest
+ */
+@RunWith(AndroidJUnit4::class)
+class HealthConnectReadWritePerfTest {
+
+ @get:Rule
+ val perfStatusReporter = PerfStatusReporter()
+
+ private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+
+ private val manager by lazy {
+ requireNotNull(context.getSystemService(HealthConnectManager::class.java))
+ }
+
+ /**
+ * A first empty test just to setup the test package and make sure it runs properly.
+ */
+ @Test
+ fun placeholder() {
+ val state = perfStatusReporter.benchmarkState
+ while (state.keepRunning()) {
+ SystemClock.sleep(100)
+ }
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
index 4352c8a..ea10690 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
@@ -24,11 +24,11 @@
import android.content.res.TypedArray
import android.perftests.utils.BenchmarkState
import android.perftests.utils.PerfStatusReporter
-import android.util.ArraySet
import androidx.test.filters.LargeTest
import com.android.internal.pm.parsing.pkg.PackageImpl
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
import com.android.internal.util.ConcurrentUtils
+import com.android.server.SystemConfig
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.ArrayBlockingQueue
@@ -217,8 +217,10 @@
isCoreApp,
this,
)
- override fun getHiddenApiWhitelistedApps() = ArraySet<String>()
- override fun getInstallConstraintsAllowlist() = ArraySet<String>()
+ override fun getHiddenApiWhitelistedApps() =
+ SystemConfig.getInstance().hiddenApiWhitelistedApps
+ override fun getInstallConstraintsAllowlist() =
+ SystemConfig.getInstance().installConstraintsAllowlist
})
override fun parseImpl(file: File) =
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 900c902..d940e38 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1830,7 +1830,12 @@
/* system_measured_calling_download_bytes */0,
/* system_measured_calling_upload_bytes */ 0,
jobStatus.getJob().getIntervalMillis(),
- jobStatus.getJob().getFlexMillis());
+ jobStatus.getJob().getFlexMillis(),
+ jobStatus.hasFlexibilityConstraint(),
+ /* isFlexConstraintSatisfied */ false,
+ jobStatus.canApplyTransportAffinities(),
+ jobStatus.getNumAppliedFlexibleConstraints(),
+ jobStatus.getNumDroppedFlexibleConstraints());
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -2273,7 +2278,12 @@
/* system_measured_calling_download_bytes */0,
/* system_measured_calling_upload_bytes */ 0,
cancelled.getJob().getIntervalMillis(),
- cancelled.getJob().getFlexMillis());
+ cancelled.getJob().getFlexMillis(),
+ cancelled.hasFlexibilityConstraint(),
+ cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
+ cancelled.canApplyTransportAffinities(),
+ cancelled.getNumAppliedFlexibleConstraints(),
+ cancelled.getNumDroppedFlexibleConstraints());
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
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 3addf9f..fe55e27 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -536,7 +536,12 @@
/* system_measured_calling_download_bytes */ 0,
/* system_measured_calling_upload_bytes */ 0,
job.getJob().getIntervalMillis(),
- job.getJob().getFlexMillis());
+ job.getJob().getFlexMillis(),
+ job.hasFlexibilityConstraint(),
+ job.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
+ job.canApplyTransportAffinities(),
+ job.getNumAppliedFlexibleConstraints(),
+ job.getNumDroppedFlexibleConstraints());
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1620,7 +1625,12 @@
TrafficStats.getUidTxBytes(completedJob.getUid())
- mInitialUploadedBytesFromCalling,
completedJob.getJob().getIntervalMillis(),
- completedJob.getJob().getFlexMillis());
+ completedJob.getJob().getFlexMillis(),
+ completedJob.hasFlexibilityConstraint(),
+ completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
+ completedJob.canApplyTransportAffinities(),
+ completedJob.getNumAppliedFlexibleConstraints(),
+ completedJob.getNumDroppedFlexibleConstraints());
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 0d5d11e..bdc2246 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -106,11 +106,8 @@
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static final int CONSTRAINT_BATTERY_NOT_LOW =
JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -125,7 +122,7 @@
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
static final int CONSTRAINT_PREFETCH = 1 << 23;
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
- static final int CONSTRAINT_FLEXIBLE = 1 << 21; // Implicit constraint
+ public static final int CONSTRAINT_FLEXIBLE = 1 << 21; // Implicit constraint
private static final int IMPLICIT_CONSTRAINTS = 0
| CONSTRAINT_BACKGROUND_NOT_RESTRICTED
@@ -1534,7 +1531,7 @@
/**
* Returns the number of required flexible job constraints that have been dropped with time.
- * The lower this number is the easier it is for the flexibility constraint to be satisfied.
+ * The higher this number is the easier it is for the flexibility constraint to be satisfied.
*/
public int getNumDroppedFlexibleConstraints() {
return mNumDroppedFlexibleConstraints;
@@ -1600,7 +1597,8 @@
mTransportAffinitiesSatisfied = isSatisfied;
}
- boolean canApplyTransportAffinities() {
+ /** Whether transport affinities can be applied to the job in flex scheduling. */
+ public boolean canApplyTransportAffinities() {
return mCanApplyTransportAffinities;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 913a76a..4d4e340 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -591,6 +591,16 @@
if (idle) {
newBucket = IDLE_BUCKET_CUTOFF;
reason = REASON_MAIN_FORCED_BY_USER;
+ final AppUsageHistory appHistory = getAppUsageHistory(packageName, userId,
+ elapsedRealtime);
+ // Wipe all expiry times that could raise the bucket on reevaluation.
+ if (appHistory.bucketExpiryTimesMs != null) {
+ for (int i = appHistory.bucketExpiryTimesMs.size() - 1; i >= 0; --i) {
+ if (appHistory.bucketExpiryTimesMs.keyAt(i) < newBucket) {
+ appHistory.bucketExpiryTimesMs.removeAt(i);
+ }
+ }
+ }
} else {
newBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index bd5c006..e589c21 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -59,6 +59,7 @@
import static com.android.server.usage.AppIdleHistory.STANDBY_BUCKET_UNKNOWN;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -2146,6 +2147,15 @@
}
}
+ /**
+ * Flush the handler.
+ * Returns true if successfully flushed within the timeout, otherwise return false.
+ */
+ @VisibleForTesting
+ boolean flushHandler(@DurationMillisLong long timeoutMillis) {
+ return mHandler.runWithScissors(() -> {}, timeoutMillis);
+ }
+
@Override
public void flushToDisk() {
synchronized (mAppIdleLock) {
diff --git a/api/Android.bp b/api/Android.bp
index 9029d25..363197a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -307,10 +307,6 @@
"framework-protos",
],
flags: [
- "--api-lint-ignore-prefix android.icu.",
- "--api-lint-ignore-prefix java.",
- "--api-lint-ignore-prefix junit.",
- "--api-lint-ignore-prefix org.",
"--error NoSettingsProvider",
"--error UnhiddenSystemApi",
"--error UnflaggedApi",
diff --git a/core/api/current.txt b/core/api/current.txt
index 12a6f74..7ab234a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9315,10 +9315,14 @@
public final class StorageStats implements android.os.Parcelable {
method public int describeContents();
method public long getAppBytes();
+ method @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public long getAppBytesByDataType(int);
method public long getCacheBytes();
method public long getDataBytes();
method public long getExternalCacheBytes();
method public void writeToParcel(android.os.Parcel, int);
+ field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_FILE_TYPE_APK = 0; // 0x0
+ field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_FILE_TYPE_DM = 1; // 0x1
+ field @FlaggedApi("android.app.usage.get_app_bytes_by_data_type_api") public static final int APP_DATA_TYPE_LIB = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR;
}
@@ -12928,6 +12932,7 @@
field public static final String FEATURE_MIDI = "android.software.midi";
field public static final String FEATURE_NFC = "android.hardware.nfc";
field public static final String FEATURE_NFC_BEAM = "android.sofware.nfc.beam";
+ field @FlaggedApi("android.nfc.enable_nfc_charging") public static final String FEATURE_NFC_CHARGING = "android.hardware.nfc.charging";
field public static final String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
field public static final String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
field public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
@@ -13418,12 +13423,12 @@
public final class SigningInfo implements android.os.Parcelable {
ctor public SigningInfo();
- ctor @FlaggedApi("android.content.pm.archiving") public SigningInfo(@IntRange(from=0) int, @Nullable java.util.Collection<android.content.pm.Signature>, @Nullable java.util.Collection<java.security.PublicKey>, @Nullable java.util.Collection<android.content.pm.Signature>);
+ ctor @FlaggedApi("android.content.pm.archiving") public SigningInfo(int, @Nullable java.util.Collection<android.content.pm.Signature>, @Nullable java.util.Collection<java.security.PublicKey>, @Nullable java.util.Collection<android.content.pm.Signature>);
ctor public SigningInfo(android.content.pm.SigningInfo);
method public int describeContents();
method public android.content.pm.Signature[] getApkContentsSigners();
method @FlaggedApi("android.content.pm.archiving") @NonNull public java.util.Collection<java.security.PublicKey> getPublicKeys();
- method @FlaggedApi("android.content.pm.archiving") @IntRange(from=0) public int getSchemeVersion();
+ method @FlaggedApi("android.content.pm.archiving") public int getSchemeVersion();
method public android.content.pm.Signature[] getSigningCertificateHistory();
method public boolean hasMultipleSigners();
method public boolean hasPastSigningCertificates();
@@ -23972,6 +23977,7 @@
method @Nullable public android.net.Uri getIconUri();
method @NonNull public String getId();
method @NonNull public CharSequence getName();
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
method public int getType();
method public int getVolume();
method public int getVolumeHandling();
@@ -23989,6 +23995,9 @@
field public static final String FEATURE_REMOTE_VIDEO_PLAYBACK = "android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2; // 0x2
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0; // 0x0
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1; // 0x1
field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2
@@ -24029,6 +24038,7 @@
method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityPublic();
method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityRestricted(@NonNull java.util.Set<java.lang.String>);
@@ -24242,6 +24252,7 @@
method public void release();
method public void selectRoute(@NonNull android.media.MediaRoute2Info);
method public void setVolume(int);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public boolean wasTransferRequestedBySelf();
}
public abstract static class MediaRouter2.TransferCallback {
@@ -24639,12 +24650,16 @@
method @Nullable public CharSequence getName();
method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getTransferReason();
method @NonNull public java.util.List<java.lang.String> getTransferableRoutes();
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.RoutingSessionInfo> CREATOR;
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_APP = 2; // 0x2
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_FALLBACK = 0; // 0x0
+ field @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1; // 0x1
}
public static final class RoutingSessionInfo.Builder {
@@ -24665,6 +24680,8 @@
method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
method @NonNull public android.media.RoutingSessionInfo.Builder setName(@Nullable CharSequence);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferInitiator(@Nullable android.os.UserHandle, @Nullable String);
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.RoutingSessionInfo.Builder setTransferReason(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeMax(int);
@@ -28797,6 +28814,7 @@
method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
+ method @FlaggedApi("android.nfc.enable_nfc_charging") @Nullable public android.nfc.WlcLDeviceInfo getWlcLDeviceInfo();
method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
method public boolean isEnabled();
method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported();
@@ -28804,6 +28822,7 @@
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
method public boolean isSecureNfcEnabled();
method public boolean isSecureNfcSupported();
+ method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -28889,6 +28908,20 @@
ctor public TagLostException(String);
}
+ @FlaggedApi("android.nfc.enable_nfc_charging") public final class WlcLDeviceInfo implements android.os.Parcelable {
+ ctor public WlcLDeviceInfo(double, double, double, int);
+ method public int describeContents();
+ method public double getBatteryLevel();
+ method public double getProductId();
+ method public int getState();
+ method public double getTemperature();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CONNECTED_CHARGING = 2; // 0x2
+ field public static final int CONNECTED_DISCHARGING = 3; // 0x3
+ field @NonNull public static final android.os.Parcelable.Creator<android.nfc.WlcLDeviceInfo> CREATOR;
+ field public static final int DISCONNECTED = 1; // 0x1
+ }
+
}
package android.nfc.cardemulation {
@@ -33358,6 +33391,7 @@
public class Process {
ctor public Process();
+ method public static final int getAppUidForSdkSandboxUid(int);
method public static final long getElapsedCpuTime();
method public static final int[] getExclusiveCores();
method public static final int getGidForName(String);
@@ -33372,6 +33406,7 @@
method public static final boolean isIsolated();
method public static final boolean isIsolatedUid(int);
method public static final boolean isSdkSandbox();
+ method public static final boolean isSdkSandboxUid(int);
method public static final void killProcess(int);
method public static final int myPid();
method @NonNull public static String myProcessName();
@@ -35874,19 +35909,19 @@
field public static final String NAMESPACE = "data2";
}
- public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getProtocolLabel(android.content.res.Resources, int, CharSequence);
- method public static int getProtocolLabelResource(int);
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
- method public static int getTypeLabelResource(int);
- field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
- field public static final String CUSTOM_PROTOCOL = "data6";
+ @Deprecated public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+ method @Deprecated public static CharSequence getProtocolLabel(android.content.res.Resources, int, CharSequence);
+ method @Deprecated public static int getProtocolLabelResource(int);
+ method @Deprecated public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
+ method @Deprecated public static int getTypeLabelResource(int);
+ field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
+ field @Deprecated public static final String CUSTOM_PROTOCOL = "data6";
field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
- field public static final String PROTOCOL = "data5";
+ field @Deprecated public static final String PROTOCOL = "data5";
field @Deprecated public static final int PROTOCOL_AIM = 0; // 0x0
- field public static final int PROTOCOL_CUSTOM = -1; // 0xffffffff
+ field @Deprecated public static final int PROTOCOL_CUSTOM = -1; // 0xffffffff
field @Deprecated public static final int PROTOCOL_GOOGLE_TALK = 5; // 0x5
field @Deprecated public static final int PROTOCOL_ICQ = 6; // 0x6
field @Deprecated public static final int PROTOCOL_JABBER = 7; // 0x7
@@ -35895,9 +35930,9 @@
field @Deprecated public static final int PROTOCOL_QQ = 4; // 0x4
field @Deprecated public static final int PROTOCOL_SKYPE = 3; // 0x3
field @Deprecated public static final int PROTOCOL_YAHOO = 2; // 0x2
- field public static final int TYPE_HOME = 1; // 0x1
- field public static final int TYPE_OTHER = 3; // 0x3
- field public static final int TYPE_WORK = 2; // 0x2
+ field @Deprecated public static final int TYPE_HOME = 1; // 0x1
+ field @Deprecated public static final int TYPE_OTHER = 3; // 0x3
+ field @Deprecated public static final int TYPE_WORK = 2; // 0x2
}
public static final class ContactsContract.CommonDataKinds.Nickname implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
@@ -36012,17 +36047,17 @@
field public static final int TYPE_SPOUSE = 14; // 0xe
}
- public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- method public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
- method public static int getTypeLabelResource(int);
- field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
+ @Deprecated public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+ method @Deprecated public static CharSequence getTypeLabel(android.content.res.Resources, int, @Nullable CharSequence);
+ method @Deprecated public static int getTypeLabelResource(int);
+ field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
field public static final String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
field public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
- field public static final String SIP_ADDRESS = "data1";
- field public static final int TYPE_HOME = 1; // 0x1
- field public static final int TYPE_OTHER = 3; // 0x3
- field public static final int TYPE_WORK = 2; // 0x2
+ field @Deprecated public static final String SIP_ADDRESS = "data1";
+ field @Deprecated public static final int TYPE_HOME = 1; // 0x1
+ field @Deprecated public static final int TYPE_OTHER = 3; // 0x3
+ field @Deprecated public static final int TYPE_WORK = 2; // 0x2
}
public static final class ContactsContract.CommonDataKinds.StructuredName implements android.provider.ContactsContract.DataColumnsWithJoins {
@@ -43277,6 +43312,7 @@
field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
@@ -43403,6 +43439,7 @@
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT = "parameters_used_for_ntn_lte_signal_bar_int";
field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
+ field @FlaggedApi("com.android.internal.telephony.flags.hide_prefer_3g_item") public static final String KEY_PREFER_3G_VISIBILITY_BOOL = "prefer_3g_visibility_bool";
field public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT = "premium_capability_maximum_daily_notification_count_int";
field public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT = "premium_capability_maximum_monthly_notification_count_int";
field public static final String KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG = "premium_capability_network_setup_time_millis_long";
@@ -45271,6 +45308,7 @@
method @Nullable public String getMncString();
method @Deprecated public String getNumber();
method public int getPortIndex();
+ method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities();
method public int getSimSlotIndex();
method public int getSubscriptionId();
method public int getSubscriptionType();
@@ -45352,6 +45390,9 @@
field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2
field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3
field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1
field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
@@ -45600,6 +45641,8 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled();
+ method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceSmsCapable();
+ method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceVoiceCapable();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
@@ -45609,9 +45652,9 @@
method @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE) public boolean isPremiumCapabilityAvailableForPurchase(int);
method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
method public boolean isRttSupported();
- method public boolean isSmsCapable();
+ method @Deprecated public boolean isSmsCapable();
method @Deprecated public boolean isTtyModeSupported();
- method public boolean isVoiceCapable();
+ method @Deprecated public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
@@ -45658,6 +45701,7 @@
field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
+ field @FlaggedApi("com.android.internal.telephony.flags.reset_mobile_network_settings") public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
@@ -53524,6 +53568,7 @@
method public android.transition.Transition getExitTransition();
method protected final int getFeatures();
method protected final int getForcedWindowFlags();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled();
method @Nullable public android.view.WindowInsetsController getInsetsController();
method @NonNull public abstract android.view.LayoutInflater getLayoutInflater();
method protected final int getLocalFeatures();
@@ -53601,6 +53646,7 @@
method public abstract void setFeatureInt(int, int);
method public void setFlags(int, int);
method public void setFormat(int);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean);
method public void setGravity(int);
method @RequiresPermission(android.Manifest.permission.HIDE_OVERLAY_WINDOWS) public final void setHideOverlayWindows(boolean);
method public void setIcon(@DrawableRes int);
@@ -53945,6 +53991,7 @@
method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public float getDesiredHdrHeadroom();
method public int getFitInsetsSides();
method public int getFitInsetsTypes();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled();
method public final CharSequence getTitle();
method public boolean isFitInsetsIgnoringVisibility();
method public boolean isHdrConversionEnabled();
@@ -53956,6 +54003,7 @@
method public void setFitInsetsIgnoringVisibility(boolean);
method public void setFitInsetsSides(int);
method public void setFitInsetsTypes(int);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean);
method public void setHdrConversionEnabled(boolean);
method public final void setTitle(CharSequence);
method public void setWallpaperTouchEventsEnabled(boolean);
@@ -57383,7 +57431,7 @@
method public abstract boolean getBuiltInZoomControls();
method public abstract int getCacheMode();
method public abstract String getCursiveFontFamily();
- method public abstract boolean getDatabaseEnabled();
+ method @Deprecated public abstract boolean getDatabaseEnabled();
method @Deprecated public abstract String getDatabasePath();
method public abstract int getDefaultFixedFontSize();
method public abstract int getDefaultFontSize();
@@ -57429,7 +57477,7 @@
method public abstract void setBuiltInZoomControls(boolean);
method public abstract void setCacheMode(int);
method public abstract void setCursiveFontFamily(String);
- method public abstract void setDatabaseEnabled(boolean);
+ method @Deprecated public abstract void setDatabaseEnabled(boolean);
method @Deprecated public abstract void setDatabasePath(String);
method public abstract void setDefaultFixedFontSize(int);
method public abstract void setDefaultFontSize(int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index de330de..c1b9f64 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -395,8 +395,6 @@
}
public class Process {
- method public static final int getAppUidForSdkSandboxUid(int);
- method public static final boolean isSdkSandboxUid(int);
method public static final int toSdkSandboxUid(int);
field public static final int NFC_UID = 1027; // 0x403
field public static final int VPN_UID = 1016; // 0x3f8
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8ce3a8d..0d4169f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3350,14 +3350,14 @@
}
@FlaggedApi("android.companion.virtual.flags.virtual_camera") public interface VirtualCameraCallback {
- method public void onProcessCaptureRequest(int, long, @Nullable android.companion.virtual.camera.VirtualCameraMetadata);
+ method public default void onProcessCaptureRequest(int, long);
method public void onStreamClosed(int);
method public void onStreamConfigured(int, @NonNull android.view.Surface, @NonNull android.companion.virtual.camera.VirtualCameraStreamConfig);
}
@FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraConfig implements android.os.Parcelable {
method public int describeContents();
- method @StringRes public int getDisplayNameStringRes();
+ method @NonNull public String getName();
method @NonNull public java.util.Set<android.companion.virtual.camera.VirtualCameraStreamConfig> getStreamConfigs();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraConfig> CREATOR;
@@ -3367,16 +3367,10 @@
ctor public VirtualCameraConfig.Builder();
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder addStreamConfig(int, int, int);
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig build();
- method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setDisplayNameStringRes(@StringRes int);
+ method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setName(@NonNull String);
method @NonNull public android.companion.virtual.camera.VirtualCameraConfig.Builder setVirtualCameraCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.camera.VirtualCameraCallback);
}
- @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraMetadata implements android.os.Parcelable {
- method public int describeContents();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.camera.VirtualCameraMetadata> CREATOR;
- }
-
@FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCameraStreamConfig implements android.os.Parcelable {
ctor public VirtualCameraStreamConfig(@IntRange(from=1) int, @IntRange(from=1) int, int);
method public int describeContents();
@@ -6478,6 +6472,7 @@
method public void clearAudioServerStateCallback();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public int dispatchAudioFocusChangeWithFade(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy, @NonNull java.util.List<android.media.AudioFocusInfo>, @Nullable android.media.FadeManagerConfiguration);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getActiveAssistantServicesUids();
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getAssistantServicesUids();
@@ -6677,6 +6672,69 @@
field public static final int CONTENT_ID_NONE = 0; // 0x0
}
+ @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") public final class FadeManagerConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributesWithVolumeShaperConfigs();
+ method public long getFadeInDelayForOffenders();
+ method public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public long getFadeInDurationForUsage(int);
+ method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(int);
+ method public long getFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public long getFadeOutDurationForUsage(int);
+ method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(int);
+ method public int getFadeState();
+ method @NonNull public java.util.List<java.lang.Integer> getFadeableUsages();
+ method @NonNull public java.util.List<android.media.AudioAttributes> getUnfadeableAudioAttributes();
+ method @NonNull public java.util.List<java.lang.Integer> getUnfadeableContentTypes();
+ method @NonNull public java.util.List<java.lang.Integer> getUnfadeablePlayerTypes();
+ method @NonNull public java.util.List<java.lang.Integer> getUnfadeableUids();
+ method public boolean isAudioAttributesUnfadeable(@NonNull android.media.AudioAttributes);
+ method public boolean isContentTypeUnfadeable(int);
+ method public boolean isFadeEnabled();
+ method public boolean isPlayerTypeUnfadeable(int);
+ method public boolean isUidUnfadeable(int);
+ method public boolean isUsageFadeable(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.FadeManagerConfiguration> CREATOR;
+ field public static final long DURATION_NOT_SET = 0L; // 0x0L
+ field public static final int FADE_STATE_DISABLED = 0; // 0x0
+ field public static final int FADE_STATE_ENABLED_AUTO = 2; // 0x2
+ field public static final int FADE_STATE_ENABLED_DEFAULT = 1; // 0x1
+ field public static final String TAG = "FadeManagerConfiguration";
+ field public static final int VOLUME_SHAPER_SYSTEM_FADE_ID = 2; // 0x2
+ }
+
+ public static final class FadeManagerConfiguration.Builder {
+ ctor public FadeManagerConfiguration.Builder();
+ ctor public FadeManagerConfiguration.Builder(long, long);
+ ctor public FadeManagerConfiguration.Builder(@NonNull android.media.FadeManagerConfiguration);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder addFadeableUsage(int);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableContentType(int);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableUid(int);
+ method @NonNull public android.media.FadeManagerConfiguration build();
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsage(int);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentType(int);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUid(int);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForUsage(int, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeState(int);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeableUsages(@NonNull java.util.List<java.lang.Integer>);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setUnfadeableAudioAttributes(@NonNull java.util.List<android.media.AudioAttributes>);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setUnfadeableContentTypes(@NonNull java.util.List<java.lang.Integer>);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setUnfadeableUids(@NonNull java.util.List<java.lang.Integer>);
+ }
+
public class HwAudioSource {
method public boolean isPlaying();
method public void start();
@@ -6895,15 +6953,18 @@
public class AudioPolicy {
method public int attachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
+ method @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public int clearFadeManagerConfigurationForFocusLoss();
method public android.media.AudioRecord createAudioRecordSink(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
+ method @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public android.media.FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss();
method public int getFocusDuckingBehavior();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioFocusInfo> getFocusStack();
method public int getStatus();
method public boolean removeUidDeviceAffinity(int);
method public boolean removeUserIdDeviceAffinity(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull android.media.AudioFocusInfo) throws java.lang.IllegalStateException;
+ method @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public int setFadeManagerConfigurationForFocusLoss(@NonNull android.media.FadeManagerConfiguration);
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
@@ -9823,17 +9884,20 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
+ method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableWlc(boolean);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagIntentAppPreferenceSupported();
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+ method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+ method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener);
field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
@@ -9848,6 +9912,10 @@
method public boolean onUnlockAttempted(android.nfc.Tag);
}
+ @FlaggedApi("android.nfc.enable_nfc_charging") public static interface NfcAdapter.WlcStateListener {
+ method public void onWlcStateChanged(@NonNull android.nfc.WlcLDeviceInfo);
+ }
+
}
package android.nfc.cardemulation {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 42daea2..ad8b685 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -369,11 +369,14 @@
}
public class NotificationManager {
+ method @FlaggedApi("android.app.modes_api") @NonNull public String addAutomaticZenRule(@NonNull android.app.AutomaticZenRule, boolean);
method public void cleanUpCallersAfter(long);
method public android.content.ComponentName getEffectsSuppressor();
method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
+ method @FlaggedApi("android.app.modes_api") public boolean removeAutomaticZenRule(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean);
+ method @FlaggedApi("android.app.modes_api") public boolean updateAutomaticZenRule(@NonNull String, @NonNull android.app.AutomaticZenRule, boolean);
method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
}
@@ -507,6 +510,7 @@
method public int getActivityType();
method @Nullable public android.graphics.Rect getAppBounds();
method @NonNull public android.graphics.Rect getBounds();
+ method public int getDisplayRotation();
method @NonNull public android.graphics.Rect getMaxBounds();
method public int getRotation();
method public int getWindowingMode();
@@ -2339,9 +2343,7 @@
}
public class Process {
- method public static final int getAppUidForSdkSandboxUid(int);
method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
- method public static final boolean isSdkSandboxUid(int);
method public static final int toSdkSandboxUid(int);
field public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; // 0x15f90
field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
@@ -3608,6 +3610,10 @@
method @Nullable public android.view.View getStatusBarBackgroundView();
}
+ public static final class WindowInsets.Type {
+ method @NonNull public static String toString(int);
+ }
+
public interface WindowManager extends android.view.ViewManager {
method public default int getDisplayImePolicy(int);
method public static boolean hasWindowExtensionsEnabled();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ea9bb39..232fc92 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -537,8 +537,8 @@
/**
* Returns whether the given user requires credential entry at this time. This is used to
- * intercept activity launches for locked work apps due to work challenge being triggered or
- * when the profile user is yet to be unlocked.
+ * intercept activity launches for apps corresponding to locked profiles due to separate
+ * challenge being triggered or when the profile user is yet to be unlocked.
*/
public abstract boolean shouldConfirmCredentials(@UserIdInt int userId);
@@ -1164,6 +1164,30 @@
*/
public abstract void logFgsApiEnd(int apiType, int uid, int pid);
+ /**
+ * Checks whether an app will be able to start a foreground service or not.
+ *
+ * @param pid The process id belonging to the app to be checked.
+ * @param uid The UID of the app to be checked.
+ * @param packageName The package name of the app to be checked.
+ * @return whether the app will be able to start a foreground service or not.
+ */
+ public abstract boolean canStartForegroundService(
+ int pid, int uid, @NonNull String packageName);
+
+ /**
+ * Returns {@code true} if a foreground service started by an uid is allowed to have
+ * while-in-use permissions.
+ *
+ * @param pid The process id belonging to the app to be checked.
+ * @param uid The UID of the app to be checked.
+ * @param packageName The package name of the app to be checked.
+ * @return whether the foreground service is allowed to have while-in-use permissions.
+ * @hide
+ */
+ public abstract boolean canAllowWhileInUsePermissionInFgs(
+ int pid, int uid, @NonNull String packageName);
+
/**
* Temporarily allow foreground service started by an uid to have while-in-use permission
* for durationMs.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6ddb36a..7e5326e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -208,9 +208,11 @@
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationSpec;
import android.webkit.WebView;
+import android.window.ITaskFragmentOrganizer;
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
import android.window.SplashScreenView;
+import android.window.TaskFragmentTransaction;
import android.window.WindowContextInfo;
import android.window.WindowProviderService;
import android.window.WindowTokenClientController;
@@ -407,7 +409,7 @@
private int mLastSessionId;
// Holds the value of the last reported device ID value from the server for the top activity.
- int mLastReportedDeviceId;
+ int mLastReportedDeviceId = Context.DEVICE_ID_DEFAULT;
final ArrayMap<IBinder, CreateServiceData> mServicesData = new ArrayMap<>();
@UnsupportedAppUsage
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
@@ -2047,6 +2049,14 @@
}
@Override
+ public void scheduleTaskFragmentTransaction(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragmentTransaction transaction) throws RemoteException {
+ // TODO(b/260873529): ITaskFragmentOrganizer can be cleanup to be a IBinder token
+ // after flag removal.
+ organizer.onTransactionReady(transaction);
+ }
+
+ @Override
public void requestDirectActions(@NonNull IBinder activityToken,
@NonNull IVoiceInteractor interactor, @Nullable RemoteCallback cancellationCallback,
@NonNull RemoteCallback callback) {
@@ -4856,10 +4866,13 @@
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
if (!service.isUiContext()) { // WindowProviderService is a UI Context.
- VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
- if (mLastReportedDeviceId == Context.DEVICE_ID_DEFAULT
- || vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) {
+ if (mLastReportedDeviceId == Context.DEVICE_ID_DEFAULT) {
service.updateDeviceId(mLastReportedDeviceId);
+ } else {
+ VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
+ if (vdm != null && vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) {
+ service.updateDeviceId(mLastReportedDeviceId);
+ }
}
}
service.onCreate();
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index a998ff2..0bae5e6 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -97,6 +97,11 @@
public boolean isUserFullscreenOverrideEnabled;
/**
+ * Whether the system has forced the activity to be fullscreen
+ */
+ public boolean isSystemFullscreenOverrideEnabled;
+
+ /**
* Hint about the letterbox state of the top activity.
*/
public boolean topActivityBoundsLetterboxed;
@@ -202,7 +207,8 @@
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
&& topActivityLetterboxHorizontalPosition
== that.topActivityLetterboxHorizontalPosition
- && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+ && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+ && isSystemFullscreenOverrideEnabled == that.isSystemFullscreenOverrideEnabled;
}
/**
@@ -224,7 +230,8 @@
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
&& cameraCompatControlState == that.cameraCompatControlState
- && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+ && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+ && isSystemFullscreenOverrideEnabled == that.isSystemFullscreenOverrideEnabled;
}
/**
@@ -243,6 +250,7 @@
topActivityLetterboxWidth = source.readInt();
topActivityLetterboxHeight = source.readInt();
isUserFullscreenOverrideEnabled = source.readBoolean();
+ isSystemFullscreenOverrideEnabled = source.readBoolean();
}
/**
@@ -262,6 +270,7 @@
dest.writeInt(topActivityLetterboxWidth);
dest.writeInt(topActivityLetterboxHeight);
dest.writeBoolean(isUserFullscreenOverrideEnabled);
+ dest.writeBoolean(isSystemFullscreenOverrideEnabled);
}
@Override
@@ -280,6 +289,7 @@
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled
+ + " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled
+ " cameraCompatControlState="
+ cameraCompatControlStateToString(cameraCompatControlState)
+ "}";
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 5541e7a..a301c18 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -48,6 +48,8 @@
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationSpec;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentTransaction;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -157,6 +159,8 @@
void scheduleApplicationInfoChanged(in ApplicationInfo ai);
void setNetworkBlockSeq(long procStateSeq);
void scheduleTransaction(in ClientTransaction transaction);
+ void scheduleTaskFragmentTransaction(in ITaskFragmentOrganizer organizer,
+ in TaskFragmentTransaction transaction);
void requestDirectActions(IBinder activityToken, IVoiceInteractor intractor,
in RemoteCallback cancellationCallback, in RemoteCallback callback);
void performDirectAction(IBinder activityToken, String actionId,
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 9438571..c3adbc3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -167,7 +167,7 @@
void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
int getInterruptionFilterFromListener(in INotificationListener token);
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
- void setInterruptionFilter(String pkg, int interruptionFilter);
+ void setInterruptionFilter(String pkg, int interruptionFilter, boolean fromUser);
void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group);
void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel);
@@ -205,11 +205,11 @@
@UnsupportedAppUsage
ZenModeConfig getZenModeConfig();
NotificationManager.Policy getConsolidatedNotificationPolicy();
- oneway void setZenMode(int mode, in Uri conditionId, String reason);
+ oneway void setZenMode(int mode, in Uri conditionId, String reason, boolean fromUser);
oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
boolean isNotificationPolicyAccessGranted(String pkg);
NotificationManager.Policy getNotificationPolicy(String pkg);
- void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
+ void setNotificationPolicy(String pkg, in NotificationManager.Policy policy, boolean fromUser);
boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
void setNotificationPolicyAccessGranted(String pkg, boolean granted);
void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
@@ -217,10 +217,10 @@
Map<String, AutomaticZenRule> getAutomaticZenRules();
// TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
List<ZenModeConfig.ZenRule> getZenRules();
- String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg);
- boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule);
- boolean removeAutomaticZenRule(String id);
- boolean removeAutomaticZenRules(String packageName);
+ String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg, boolean fromUser);
+ boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule, boolean fromUser);
+ boolean removeAutomaticZenRule(String id, boolean fromUser);
+ boolean removeAutomaticZenRules(String packageName, boolean fromUser);
int getRuleInstanceCount(in ComponentName owner);
void setAutomaticZenRuleState(String id, in Condition condition);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a510c77..476232c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7733,11 +7733,12 @@
} else if (mPictureIcon.getType() == Icon.TYPE_BITMAP) {
// If the icon contains a bitmap, use the old extra so that listeners which look
// for that extra can still find the picture. Don't include the new extra in
- // that case, to avoid duplicating data.
+ // that case, to avoid duplicating data. Leave the unused extra set to null to avoid
+ // crashing apps that came to expect it to be present but null.
extras.putParcelable(EXTRA_PICTURE, mPictureIcon.getBitmap());
- extras.remove(EXTRA_PICTURE_ICON);
+ extras.putParcelable(EXTRA_PICTURE_ICON, null);
} else {
- extras.remove(EXTRA_PICTURE);
+ extras.putParcelable(EXTRA_PICTURE, null);
extras.putParcelable(EXTRA_PICTURE_ICON, mPictureIcon);
}
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index d23b16d..0b6e24c 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1184,14 +1184,20 @@
*/
@UnsupportedAppUsage
public void setZenMode(int mode, Uri conditionId, String reason) {
+ setZenMode(mode, conditionId, reason, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public void setZenMode(int mode, Uri conditionId, String reason, boolean fromUser) {
INotificationManager service = getService();
try {
- service.setZenMode(mode, conditionId, reason);
+ service.setZenMode(mode, conditionId, reason, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+
/**
* @hide
*/
@@ -1325,9 +1331,19 @@
* @return The id of the newly created rule; null if the rule could not be created.
*/
public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+ return addAutomaticZenRule(automaticZenRule, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @NonNull
+ public String addAutomaticZenRule(@NonNull AutomaticZenRule automaticZenRule,
+ boolean fromUser) {
INotificationManager service = getService();
try {
- return service.addAutomaticZenRule(automaticZenRule, mContext.getPackageName());
+ return service.addAutomaticZenRule(automaticZenRule,
+ mContext.getPackageName(), fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1347,9 +1363,17 @@
* @return Whether the rule was successfully updated.
*/
public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule) {
+ return updateAutomaticZenRule(id, automaticZenRule, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public boolean updateAutomaticZenRule(@NonNull String id,
+ @NonNull AutomaticZenRule automaticZenRule, boolean fromUser) {
INotificationManager service = getService();
try {
- return service.updateAutomaticZenRule(id, automaticZenRule);
+ return service.updateAutomaticZenRule(id, automaticZenRule, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1388,9 +1412,16 @@
* @return Whether the rule was successfully deleted.
*/
public boolean removeAutomaticZenRule(String id) {
+ return removeAutomaticZenRule(id, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public boolean removeAutomaticZenRule(@NonNull String id, boolean fromUser) {
INotificationManager service = getService();
try {
- return service.removeAutomaticZenRule(id);
+ return service.removeAutomaticZenRule(id, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1402,9 +1433,14 @@
* @hide
*/
public boolean removeAutomaticZenRules(String packageName) {
+ return removeAutomaticZenRules(packageName, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public boolean removeAutomaticZenRules(String packageName, boolean fromUser) {
INotificationManager service = getService();
try {
- return service.removeAutomaticZenRules(packageName);
+ return service.removeAutomaticZenRules(packageName, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1685,10 +1721,15 @@
*/
// TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public void setNotificationPolicy(@NonNull Policy policy) {
+ setNotificationPolicy(policy, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public void setNotificationPolicy(@NonNull Policy policy, boolean fromUser) {
checkRequired("policy", policy);
INotificationManager service = getService();
try {
- service.setNotificationPolicy(mContext.getOpPackageName(), policy);
+ service.setNotificationPolicy(mContext.getOpPackageName(), policy, fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2685,9 +2726,16 @@
*/
// TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
+ setInterruptionFilter(interruptionFilter, /* fromUser= */ false);
+ }
+
+ /** @hide */
+ public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter,
+ boolean fromUser) {
final INotificationManager service = getService();
try {
- service.setInterruptionFilter(mContext.getOpPackageName(), interruptionFilter);
+ service.setInterruptionFilter(mContext.getOpPackageName(), interruptionFilter,
+ fromUser);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 772b0b4..47d19ed 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -88,6 +88,10 @@
# Pinner
per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
+# BackgroundInstallControlManager
+per-file BackgroundInstallControlManager.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file background_install_control_manager.aconfig = file:/services/core/java/com/android/server/pm/OWNERS
+
# ResourcesManager
per-file ResourcesManager.java = file:RESOURCES_OWNERS
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 385fd50..14195c4 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -241,6 +241,23 @@
public static final int CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE = 3;
/**
+ * Broadcast action: sent to apps that hold the status bar permission when
+ * KeyguardManager#setPrivateNotificationsAllowed() is changed.
+ *
+ * Extras: #EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
+ * @hide
+ */
+ public static final String ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED
+ = "android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED";
+
+ /**
+ * Boolean, the latest value of KeyguardManager#getPrivateNotificationsAllowed()
+ * @hide
+ */
+ public static final String EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
+ = "android.app.extra.KM_PRIVATE_NOTIFS_ALLOWED";
+
+ /**
* Session flag for {@link #registerSessionListener} indicating the listener
* is interested in sessions on the keygaurd.
* Keyguard Session Boundaries:
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 4621634..aa3b71a 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -27,6 +27,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Configuration;
@@ -326,7 +327,7 @@
}
/**
- * Sets the apparent display cutout.
+ * Sets the display rotation.
* @hide
*/
public void setDisplayRotation(@Surface.Rotation int rotation) {
@@ -386,9 +387,9 @@
}
/**
- * @see #setDisplayRotation
- * @hide
+ * Gets the display rotation.
*/
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
public @Surface.Rotation int getDisplayRotation() {
return mDisplayRotation;
}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index fb0edb9..d11c6c5 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -36,3 +36,10 @@
description: "Guards the security fix that ensures all URIs in intents and Person.java are valid"
bug: "281044385"
}
+
+flag {
+ name: "keyguard_private_notifications"
+ namespace: "systemui"
+ description: "Fixes the behavior of KeyguardManager#setPrivateNotificationsAllowed()"
+ bug: "309920145"
+}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 3ce094e..cbb0ae7 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -23,6 +23,7 @@
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.os.IBinder;
import android.os.Parcel;
@@ -85,6 +86,12 @@
client.reportRelaunch(r);
}
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ return client.getActivity(getActivityToken());
+ }
+
// ObjectPoolItem implementation
private ActivityRelaunchItem() {}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 7418c06..9f97f6f 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -16,7 +16,7 @@
package android.app.servertransaction;
-import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+import static com.android.window.flags.Flags.bundleClientTransactionFlag;
import static java.util.Objects.requireNonNull;
@@ -67,7 +67,7 @@
* window configuration.
*/
public void onDisplayChanged(int displayId) {
- if (!isSyncWindowConfigUpdateFlagEnabled()) {
+ if (!isBundleClientTransactionFlagEnabled()) {
return;
}
if (ActivityThread.isSystem()) {
@@ -77,9 +77,9 @@
mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
}
- /** Whether {@link #syncWindowConfigUpdateFlag} feature flag is enabled. */
- public boolean isSyncWindowConfigUpdateFlagEnabled() {
+ /** Whether {@link #bundleClientTransactionFlag} feature flag is enabled. */
+ public boolean isBundleClientTransactionFlagEnabled() {
// Can't read flag from isolated process.
- return !Process.isIsolated() && syncWindowConfigUpdateFlag();
+ return !Process.isIsolated() && bundleClientTransactionFlag();
}
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index d2ef65a..1190bf6 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -24,12 +24,14 @@
import android.annotation.Nullable;
import android.app.ActivityClient;
import android.app.ActivityOptions;
+import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.IActivityClientController;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -115,6 +117,13 @@
client.countLaunchingActivities(-1);
}
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ // LaunchActivityItem may update the global config with #mCurConfig.
+ return ActivityThread.currentApplication();
+ }
+
// ObjectPoolItem implementation
private LaunchActivityItem() {}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 961da19..1353d16 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
@@ -55,6 +56,12 @@
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ return client.getActivity(getActivityToken());
+ }
+
// ObjectPoolItem implementation
private MoveToDisplayItem() {}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 9f5e0dc..2e47fee 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -32,7 +32,7 @@
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
-import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+import static com.android.window.flags.Flags.bundleClientTransactionFlag;
import android.annotation.NonNull;
import android.app.ActivityThread.ActivityClientRecord;
@@ -41,6 +41,8 @@
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Process;
+import android.os.Trace;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
@@ -62,8 +64,11 @@
private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
- /** Keeps track of display ids whose Configuration got updated within a transaction. */
- private final ArraySet<Integer> mConfigUpdatedDisplayIds = new ArraySet<>();
+ /**
+ * Keeps track of the Context whose Configuration got updated within a transaction, mapping to
+ * the config before the transaction.
+ */
+ private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();
/** Initialize an instance with transaction handler, that will execute all requested actions. */
public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
@@ -83,24 +88,48 @@
Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
}
- if (transaction.getTransactionItems() != null) {
- executeTransactionItems(transaction);
- } else {
- // TODO(b/260873529): cleanup after launch.
- executeCallbacks(transaction);
- executeLifecycleState(transaction);
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionExecuted");
+ try {
+ if (transaction.getTransactionItems() != null) {
+ executeTransactionItems(transaction);
+ } else {
+ // TODO(b/260873529): cleanup after launch.
+ executeCallbacks(transaction);
+ executeLifecycleState(transaction);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
- if (!mConfigUpdatedDisplayIds.isEmpty()) {
+ if (!mContextToPreChangedConfigMap.isEmpty()) {
// Whether this transaction should trigger DisplayListener#onDisplayChanged.
- final ClientTransactionListenerController controller =
- ClientTransactionListenerController.getInstance();
- final int displayCount = mConfigUpdatedDisplayIds.size();
- for (int i = 0; i < displayCount; i++) {
- final int displayId = mConfigUpdatedDisplayIds.valueAt(i);
- controller.onDisplayChanged(displayId);
+ try {
+ // Calculate display ids that have config changed.
+ final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
+ final int contextCount = mContextToPreChangedConfigMap.size();
+ for (int i = 0; i < contextCount; i++) {
+ final Context context = mContextToPreChangedConfigMap.keyAt(i);
+ final Configuration preTransactionConfig =
+ mContextToPreChangedConfigMap.valueAt(i);
+ final Configuration postTransactionConfig = context.getResources()
+ .getConfiguration();
+ if (!areConfigurationsEqualForDisplay(
+ postTransactionConfig, preTransactionConfig)) {
+ configUpdatedDisplayIds.add(context.getDisplayId());
+ }
+ }
+
+ // Dispatch the display changed callbacks.
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ final int displayCount = configUpdatedDisplayIds.size();
+ for (int i = 0; i < displayCount; i++) {
+ final int displayId = configUpdatedDisplayIds.valueAt(i);
+ controller.onDisplayChanged(displayId);
+ }
+ } finally {
+ mContextToPreChangedConfigMap.clear();
}
- mConfigUpdatedDisplayIds.clear();
}
mPendingActions.clear();
@@ -182,26 +211,24 @@
}
}
- // Can't read flag from isolated process.
- final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated()
- && syncWindowConfigUpdateFlag();
- final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled
+ final boolean shouldTrackConfigUpdatedContext =
+ // No configuration change for local transaction.
+ !mTransactionHandler.isExecutingLocalTransaction()
+ // Can't read flag from isolated process.
+ && !Process.isIsolated()
+ && bundleClientTransactionFlag();
+ final Context configUpdatedContext = shouldTrackConfigUpdatedContext
? item.getContextToUpdate(mTransactionHandler)
: null;
- final Configuration preExecutedConfig = configUpdatedContext != null
- ? new Configuration(configUpdatedContext.getResources().getConfiguration())
- : null;
+ if (configUpdatedContext != null
+ && !mContextToPreChangedConfigMap.containsKey(configUpdatedContext)) {
+ // Keep track of the first pre-executed config of each changed Context.
+ mContextToPreChangedConfigMap.put(configUpdatedContext,
+ new Configuration(configUpdatedContext.getResources().getConfiguration()));
+ }
item.execute(mTransactionHandler, mPendingActions);
- if (configUpdatedContext != null) {
- final Configuration postExecutedConfig = configUpdatedContext.getResources()
- .getConfiguration();
- if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) {
- mConfigUpdatedDisplayIds.add(configUpdatedContext.getDisplayId());
- }
- }
-
item.postExecute(mTransactionHandler, mPendingActions);
if (r == null) {
// Launch activity request will create an activity record.
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index 7d3eb87..193b03c 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -22,9 +22,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.app.ClientTransactionHandler;
+import android.content.Context;
import android.os.Parcel;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.MergedConfiguration;
import android.view.IWindow;
import android.view.InsetsState;
@@ -52,6 +55,11 @@
@Override
public void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
+ mReportDraw ? "windowResizedReport" : "windowResized");
+ if (mWindow instanceof ResizeListener listener) {
+ listener.onExecutingWindowStateResizeItem();
+ }
try {
mWindow.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
mAlwaysConsumeSystemBars, mDisplayId, mSyncSeqId, mDragResizing);
@@ -59,6 +67,14 @@
// Should be a local call.
throw new RuntimeException(e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ // WindowStateResizeItem may update the global config with #mConfiguration.
+ return ActivityThread.currentApplication();
}
// ObjectPoolItem implementation
@@ -80,7 +96,7 @@
instance.mFrames = new ClientWindowFrames(frames);
instance.mReportDraw = reportDraw;
instance.mConfiguration = new MergedConfiguration(configuration);
- instance.mInsetsState = new InsetsState(insetsState);
+ instance.mInsetsState = new InsetsState(insetsState, true /* copySources */);
instance.mForceLayout = forceLayout;
instance.mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
instance.mDisplayId = displayId;
@@ -190,4 +206,10 @@
+ ", configuration=" + mConfiguration
+ "}";
}
+
+ /** The interface for IWindow to perform resize directly if possible. */
+ public interface ResizeListener {
+ /** Notifies that IWindow#resized is going to be called from WindowStateResizeItem. */
+ void onExecutingWindowStateResizeItem();
+ }
}
diff --git a/core/java/android/app/usage/StorageStats.java b/core/java/android/app/usage/StorageStats.java
index 8d25d7b..87d97d5 100644
--- a/core/java/android/app/usage/StorageStats.java
+++ b/core/java/android/app/usage/StorageStats.java
@@ -17,11 +17,16 @@
package android.app.usage;
import android.annotation.BytesLong;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Storage statistics for a UID, package, or {@link UserHandle} on a single
* storage volume.
@@ -29,10 +34,47 @@
* @see StorageStatsManager
*/
public final class StorageStats implements Parcelable {
- /** {@hide} */ public long codeBytes;
- /** {@hide} */ public long dataBytes;
- /** {@hide} */ public long cacheBytes;
- /** {@hide} */ public long externalCacheBytes;
+ /** @hide */ public long codeBytes;
+ /** @hide */ public long dataBytes;
+ /** @hide */ public long cacheBytes;
+ /** @hide */ public long apkBytes;
+ /** @hide */ public long libBytes;
+ /** @hide */ public long dmBytes;
+ /** @hide */ public long externalCacheBytes;
+
+ /** Represents all .apk files in application code path.
+ * Can be used as an input to {@link #getAppBytesByDataType(int)}
+ * to get the sum of sizes for files of this type.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public static final int APP_DATA_TYPE_FILE_TYPE_APK = 0;
+
+ /** Represents all .dm files in application code path.
+ * Can be used as an input to {@link #getAppBytesByDataType(int)}
+ * to get the sum of sizes for files of this type.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public static final int APP_DATA_TYPE_FILE_TYPE_DM = 1;
+
+ /** Represents lib/ in application code path.
+ * Can be used as an input to {@link #getAppBytesByDataType(int)}
+ * to get the size of lib/ directory.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public static final int APP_DATA_TYPE_LIB = 2;
+
+ /**
+ * Keep in sync with the file types defined above.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ @IntDef(flag = false, value = {
+ APP_DATA_TYPE_FILE_TYPE_APK,
+ APP_DATA_TYPE_FILE_TYPE_DM,
+ APP_DATA_TYPE_LIB,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppDataType {}
/**
* Return the size of app. This includes {@code APK} files, optimized
@@ -48,6 +90,27 @@
}
/**
+ * Return the size of the specified data type. This includes files stored under
+ * application code path.
+ * <p>
+ * If there is more than one package inside a uid, the return represents the aggregated
+ * stats when query StorageStat for package or uid.
+ * The data is not collected and the return defaults to 0 when query StorageStats for user.
+ *
+ * <p>
+ * Data is isolated for each user on a multiuser device.
+ */
+ @FlaggedApi(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
+ public long getAppBytesByDataType(@AppDataType int dataType) {
+ switch (dataType) {
+ case APP_DATA_TYPE_FILE_TYPE_APK: return apkBytes;
+ case APP_DATA_TYPE_LIB: return libBytes;
+ case APP_DATA_TYPE_FILE_TYPE_DM: return dmBytes;
+ default: return 0;
+ }
+ }
+
+ /**
* Return the size of all data. This includes files stored under
* {@link Context#getDataDir()}, {@link Context#getCacheDir()},
* {@link Context#getCodeCacheDir()}.
@@ -98,6 +161,9 @@
this.codeBytes = in.readLong();
this.dataBytes = in.readLong();
this.cacheBytes = in.readLong();
+ this.apkBytes = in.readLong();
+ this.libBytes = in.readLong();
+ this.dmBytes = in.readLong();
this.externalCacheBytes = in.readLong();
}
@@ -111,6 +177,9 @@
dest.writeLong(codeBytes);
dest.writeLong(dataBytes);
dest.writeLong(cacheBytes);
+ dest.writeLong(apkBytes);
+ dest.writeLong(libBytes);
+ dest.writeLong(dmBytes);
dest.writeLong(externalCacheBytes);
}
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index a611255..4d9d911 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -35,3 +35,10 @@
description: " Feature flag to support filter based event query API"
bug: "194321117"
}
+
+flag {
+ name: "get_app_bytes_by_data_type_api"
+ namespace: "system_performance"
+ description: "Feature flag for collecting app data size by file type API"
+ bug: "294088945"
+}
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index c95b864..ec2e5fe 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -12,4 +12,11 @@
namespace: "app_widgets"
description: "Enable adapter conversion to RemoteCollectionItemsAdapter"
bug: "245950570"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "remove_app_widget_service_io_from_critical_path"
+ namespace: "app_widgets"
+ description: "Move state file IO to non-critical path"
+ bug: "312949280"
+}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index b11840e..879656a 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -220,6 +220,12 @@
*/
public static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
/**
+ * Test message type without a response.
+ *
+ * @hide
+ */
+ public static final int MESSAGE_ONEWAY_PING = 0x43807378; // +PIN
+ /**
* Message header assigned to the remote authentication handshakes.
*
* @hide
@@ -237,6 +243,18 @@
* @hide
*/
public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
+ /**
+ * Message header assigned to the one-way message sent from the wearable device.
+ *
+ * @hide
+ */
+ public static final int MESSAGE_ONEWAY_FROM_WEARABLE = 0x43708287; // +FRW
+ /**
+ * Message header assigned to the one-way message sent to the wearable device.
+ *
+ * @hide
+ */
+ public static final int MESSAGE_ONEWAY_TO_WEARABLE = 0x43847987; // +TOW
/**
* The length limit of Association tag.
diff --git a/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
index fac44b5..44942d6 100644
--- a/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
+++ b/core/java/android/companion/virtual/camera/IVirtualCameraCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -16,11 +16,11 @@
package android.companion.virtual.camera;
import android.companion.virtual.camera.VirtualCameraStreamConfig;
-import android.companion.virtual.camera.VirtualCameraMetadata;
import android.view.Surface;
/**
- * Interface for the virtual camera service and system server to talk back to the virtual camera owner.
+ * Interface for the virtual camera service and system server to talk back to the virtual camera
+ * owner.
*
* @hide
*/
@@ -40,8 +40,7 @@
in VirtualCameraStreamConfig streamConfig);
/**
- * The client application is requesting a camera frame for the given streamId with the provided
- * metadata.
+ * The client application is requesting a camera frame for the given streamId and frameId.
*
* <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
* this stream that was provided during the {@link #onStreamConfigured(int, Surface,
@@ -52,16 +51,14 @@
* VirtualCameraStreamConfig)}
* @param frameId The frameId that is being requested. Each request will have a different
* frameId, that will be increasing for each call with a particular streamId.
- * @param metadata The metadata requested for the frame. The virtual camera should do its best
- * to honor the requested metadata.
*/
- oneway void onProcessCaptureRequest(
- int streamId, long frameId, in VirtualCameraMetadata metadata);
+ oneway void onProcessCaptureRequest(int streamId, long frameId);
/**
* The stream previously configured when {@link #onStreamConfigured(int, Surface,
* VirtualCameraStreamConfig)} was called is now being closed and associated resources can be
- * freed. The Surface was disposed on the client side and should not be used anymore by the virtual camera owner
+ * freed. The Surface was disposed on the client side and should not be used anymore by the
+ * virtual camera owner.
*
* @param streamId The id of the stream that was closed.
*/
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
index a18ae03..5b658b8 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -18,7 +18,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.companion.virtual.flags.Flags;
import android.view.Surface;
@@ -50,8 +49,7 @@
@NonNull VirtualCameraStreamConfig streamConfig);
/**
- * The client application is requesting a camera frame for the given streamId with the provided
- * metadata.
+ * The client application is requesting a camera frame for the given streamId and frameId.
*
* <p>The virtual camera needs to write the frame data in the {@link Surface} corresponding to
* this stream that was provided during the {@link #onStreamConfigured(int, Surface,
@@ -62,12 +60,8 @@
* VirtualCameraStreamConfig)}
* @param frameId The frameId that is being requested. Each request will have a different
* frameId, that will be increasing for each call with a particular streamId.
- * @param metadata The metadata requested for the frame. The virtual camera should do its best
- * to honor the requested metadata but the consumer won't be informed about the metadata set
- * for a particular frame. If null, the requested frame can be anything the producer sends.
*/
- void onProcessCaptureRequest(
- int streamId, long frameId, @Nullable VirtualCameraMetadata metadata);
+ default void onProcessCaptureRequest(int streamId, long frameId) {}
/**
* The stream previously configured when {@link #onStreamConfigured(int, Surface,
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
index f1eb240..59fe9a1 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -20,11 +20,9 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.companion.virtual.flags.Flags;
-import android.content.res.Resources;
import android.graphics.ImageFormat;
import android.os.Parcel;
import android.os.Parcelable;
@@ -45,16 +43,16 @@
@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public final class VirtualCameraConfig implements Parcelable {
- private final @StringRes int mNameStringRes;
+ private final String mName;
private final Set<VirtualCameraStreamConfig> mStreamConfigurations;
private final IVirtualCameraCallback mCallback;
private VirtualCameraConfig(
- int displayNameStringRes,
+ @NonNull String name,
@NonNull Set<VirtualCameraStreamConfig> streamConfigurations,
@NonNull Executor executor,
@NonNull VirtualCameraCallback callback) {
- mNameStringRes = displayNameStringRes;
+ mName = requireNonNull(name, "Missing name");
mStreamConfigurations =
Set.copyOf(requireNonNull(streamConfigurations, "Missing stream configurations"));
if (mStreamConfigurations.isEmpty()) {
@@ -68,7 +66,7 @@
}
private VirtualCameraConfig(@NonNull Parcel in) {
- mNameStringRes = in.readInt();
+ mName = in.readString8();
mCallback = IVirtualCameraCallback.Stub.asInterface(in.readStrongBinder());
mStreamConfigurations =
Set.of(
@@ -84,18 +82,18 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mNameStringRes);
+ dest.writeString8(mName);
dest.writeStrongInterface(mCallback);
dest.writeParcelableArray(
mStreamConfigurations.toArray(new VirtualCameraStreamConfig[0]), flags);
}
/**
- * @return The display name of this VirtualCamera
+ * @return The name of this VirtualCamera
*/
- @StringRes
- public int getDisplayNameStringRes() {
- return mNameStringRes;
+ @NonNull
+ public String getName() {
+ return mName;
}
/**
@@ -126,30 +124,22 @@
* <li>At least one stream must be added with {@link #addStreamConfig(int, int, int)}.
* <li>A callback must be set with {@link #setVirtualCameraCallback(Executor,
* VirtualCameraCallback)}
- * <li>A user readable name can be set with {@link #setDisplayNameStringRes(int)}
+ * <li>A camera name must be set with {@link #setName(String)}
*/
@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public static final class Builder {
- private @StringRes int mDisplayNameStringRes = Resources.ID_NULL;
+ private String mName;
private final ArraySet<VirtualCameraStreamConfig> mStreamConfigurations = new ArraySet<>();
private Executor mCallbackExecutor;
private VirtualCameraCallback mCallback;
/**
- * Set the visible name of this camera for the user.
- *
- * <p>Sets the resource to a string representing a user readable name for this virtual
- * camera.
- *
- * @throws IllegalArgumentException if an invalid resource id is passed.
+ * Set the name of the virtual camera instance.
*/
@NonNull
- public Builder setDisplayNameStringRes(@StringRes int displayNameStringRes) {
- if (displayNameStringRes <= 0) {
- throw new IllegalArgumentException("Invalid resource passed for display name");
- }
- mDisplayNameStringRes = displayNameStringRes;
+ public Builder setName(@NonNull String name) {
+ mName = requireNonNull(name, "Display name cannot be null");
return this;
}
@@ -203,7 +193,7 @@
@NonNull
public VirtualCameraConfig build() {
return new VirtualCameraConfig(
- mDisplayNameStringRes, mStreamConfigurations, mCallbackExecutor, mCallback);
+ mName, mStreamConfigurations, mCallbackExecutor, mCallback);
}
}
@@ -224,9 +214,8 @@
}
@Override
- public void onProcessCaptureRequest(
- int streamId, long frameId, VirtualCameraMetadata metadata) {
- mExecutor.execute(() -> mCallback.onProcessCaptureRequest(streamId, frameId, metadata));
+ public void onProcessCaptureRequest(int streamId, long frameId) {
+ mExecutor.execute(() -> mCallback.onProcessCaptureRequest(streamId, frameId));
}
@Override
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java b/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
deleted file mode 100644
index 1ba36d0..0000000
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.java
+++ /dev/null
@@ -1,61 +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.companion.virtual.camera;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.companion.virtual.flags.Flags;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Data structure used to store camera metadata compatible with VirtualCamera.
- *
- * @hide
- */
-@SystemApi
-@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
-public final class VirtualCameraMetadata implements Parcelable {
-
- /** @hide */
- public VirtualCameraMetadata(@NonNull Parcel in) {}
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {}
-
- @NonNull
- public static final Creator<VirtualCameraMetadata> CREATOR =
- new Creator<>() {
- @Override
- @NonNull
- public VirtualCameraMetadata createFromParcel(Parcel in) {
- return new VirtualCameraMetadata(in);
- }
-
- @Override
- @NonNull
- public VirtualCameraMetadata[] newArray(int size) {
- return new VirtualCameraMetadata[size];
- }
- };
-}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java
index e198821..1272f16 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java
+++ b/core/java/android/companion/virtual/camera/VirtualCameraStreamConfig.java
@@ -24,6 +24,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* The configuration of a single virtual camera stream.
*
@@ -98,6 +100,19 @@
return mHeight;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ VirtualCameraStreamConfig that = (VirtualCameraStreamConfig) o;
+ return mWidth == that.mWidth && mHeight == that.mHeight && mFormat == that.mFormat;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWidth, mHeight, mFormat);
+ }
+
/** Returns the {@link ImageFormat} of this stream. */
@ImageFormat.Format
public int getFormat() {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b75c64d..fa76e39 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3547,6 +3547,8 @@
*
* @param receiver The BroadcastReceiver to unregister.
*
+ * @throws IllegalArgumentException if the {@code receiver} was not previously registered or
+ * already unregistered.
* @see #registerReceiver
*/
public abstract void unregisterReceiver(BroadcastReceiver receiver);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a863870..8151a91 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -16,6 +16,8 @@
package android.content.pm;
+import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.CheckResult;
@@ -55,7 +57,6 @@
import android.content.IntentSender;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.dex.ArtManager;
-import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -91,6 +92,10 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageInfoCommonUtils;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DataClass;
@@ -817,6 +822,8 @@
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+ MATCH_DIRECT_BOOT_AWARE,
+ MATCH_DIRECT_BOOT_UNAWARE,
GET_ATTRIBUTIONS_LONG,
})
@Retention(RetentionPolicy.SOURCE)
@@ -2518,6 +2525,7 @@
USER_MIN_ASPECT_RATIO_16_9,
USER_MIN_ASPECT_RATIO_3_2,
USER_MIN_ASPECT_RATIO_FULLSCREEN,
+ USER_MIN_ASPECT_RATIO_APP_DEFAULT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserMinAspectRatio {}
@@ -2571,6 +2579,16 @@
*/
public static final int USER_MIN_ASPECT_RATIO_FULLSCREEN = 6;
+ /**
+ * Aspect ratio override code: user sets to app's default aspect ratio.
+ * This resets both the user-forced aspect ratio, and the device manufacturer
+ * per-app override {@link ActivityInfo#OVERRIDE_ANY_ORIENTATION_TO_USER}.
+ * It is different from {@link #USER_MIN_ASPECT_RATIO_UNSET} as the latter may
+ * apply the device manufacturer per-app orientation override if any,
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_APP_DEFAULT = 7;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -3295,6 +3313,14 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports NFC charging.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_NFC_CHARGING)
+ public static final String FEATURE_NFC_CHARGING = "android.hardware.nfc.charging";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The Beam API is enabled on the device.
*/
@SdkConstant(SdkConstantType.FEATURE)
@@ -3304,7 +3330,7 @@
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports any
* one of the {@link #FEATURE_NFC}, {@link #FEATURE_NFC_HOST_CARD_EMULATION},
- * or {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF} features.
+ * {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF}, or {@link #FEATURE_NFC_CHARGING} features.
*
* @hide
*/
@@ -8609,28 +8635,56 @@
@Nullable
public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath,
@NonNull PackageInfoFlags flags) {
- long flagsBits = flags.getValue();
- final PackageParser parser = new PackageParser();
- parser.setCallback(new PackageParser.CallbackImpl(this));
final File apkFile = new File(archiveFilePath);
- try {
- if ((flagsBits & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
- // Caller expressed an explicit opinion about what encryption
- // aware/unaware components they want to see, so fall through and
- // give them what they want
- } else {
- // Caller expressed no opinion, so match everything
- flagsBits |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
- }
- PackageParser.Package pkg = parser.parsePackage(apkFile, 0, false);
- if ((flagsBits & GET_SIGNATURES) != 0 || (flagsBits & GET_SIGNING_CERTIFICATES) != 0) {
- PackageParser.collectCertificates(pkg, false /* skipVerify */);
- }
- return PackageParser.generatePackageInfo(pkg, null, (int) flagsBits, 0, 0, null,
- FrameworkPackageUserState.DEFAULT);
- } catch (PackageParser.PackageParserException e) {
- Log.w(TAG, "Failure to parse package archive", e);
+ @PackageInfoFlagsBits long flagsBits = flags.getValue();
+ if ((flagsBits & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
+ // Caller expressed an explicit opinion about what encryption
+ // aware/unaware components they want to see, so fall through and
+ // give them what they want
+ } else {
+ // Caller expressed no opinion, so match everything
+ flagsBits |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+ }
+
+ int parserFlags = 0;
+ if ((flagsBits & (GET_SIGNATURES | GET_SIGNING_CERTIFICATES)) != 0) {
+ parserFlags |= PARSE_COLLECT_CERTIFICATES;
+ }
+
+ final PackageParser2 parser2 = new PackageParser2(/*separateProcesses*/ null,
+ /*displayMetrics*/ null,/*cacher*/ null,
+ new PackageParser2.Callback() {
+ @Override
+ public boolean hasFeature(String feature) {
+ return PackageManager.this.hasSystemFeature(feature);
+ }
+
+ @NonNull
+ @Override
+ public Set<String> getHiddenApiWhitelistedApps() {
+ return Collections.emptySet();
+ }
+
+ @NonNull
+ @Override
+ public Set<String> getInstallConstraintsAllowlist() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public boolean isChangeEnabled(long changeId,
+ @androidx.annotation.NonNull ApplicationInfo appInfo) {
+ return false;
+ }
+ });
+
+ try {
+ ParsedPackage pp = parser2.parsePackage(apkFile, parserFlags, false);
+
+ return PackageInfoCommonUtils.generate(pp, flagsBits, UserHandle.myUserId());
+ } catch (PackageParserException e) {
+ Log.w(TAG, "Failure to parse package archive apkFile= " +apkFile);
return null;
}
}
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index 81cfc07..b919c4b 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -55,6 +55,18 @@
/** Size of cache used by the application. (e.g., /data/data/<app>/cache) */
public long cacheSize;
+ /** Size of .apk files of the application. */
+ /** @hide */
+ public long apkSize;
+
+ /** Size of the libraries of the application. */
+ /** @hide */
+ public long libSize;
+
+ /** Size of the .dm files of the application. */
+ /** @hide */
+ public long dmSize;
+
/**
* Size of the secure container on external storage holding the
* application's code.
@@ -108,6 +120,18 @@
sb.append(" cache=");
sb.append(cacheSize);
}
+ if (apkSize != 0) {
+ sb.append(" apk=");
+ sb.append(apkSize);
+ }
+ if (libSize != 0) {
+ sb.append(" lib=");
+ sb.append(libSize);
+ }
+ if (dmSize != 0) {
+ sb.append(" dm=");
+ sb.append(dmSize);
+ }
if (externalCodeSize != 0) {
sb.append(" extCode=");
sb.append(externalCodeSize);
@@ -149,6 +173,9 @@
codeSize = source.readLong();
dataSize = source.readLong();
cacheSize = source.readLong();
+ apkSize = source.readLong();
+ libSize = source.readLong();
+ dmSize = source.readLong();
externalCodeSize = source.readLong();
externalDataSize = source.readLong();
externalCacheSize = source.readLong();
@@ -162,6 +189,9 @@
codeSize = pStats.codeSize;
dataSize = pStats.dataSize;
cacheSize = pStats.cacheSize;
+ apkSize = pStats.apkSize;
+ libSize = pStats.libSize;
+ dmSize = pStats.dmSize;
externalCodeSize = pStats.externalCodeSize;
externalDataSize = pStats.externalDataSize;
externalCacheSize = pStats.externalCacheSize;
@@ -179,6 +209,9 @@
dest.writeLong(codeSize);
dest.writeLong(dataSize);
dest.writeLong(cacheSize);
+ dest.writeLong(apkSize);
+ dest.writeLong(libSize);
+ dest.writeLong(dmSize);
dest.writeLong(externalCodeSize);
dest.writeLong(externalDataSize);
dest.writeLong(externalCacheSize);
@@ -198,6 +231,9 @@
&& codeSize == otherStats.codeSize
&& dataSize == otherStats.dataSize
&& cacheSize == otherStats.cacheSize
+ && apkSize == otherStats.apkSize
+ && libSize == otherStats.libSize
+ && dmSize == otherStats.dmSize
&& externalCodeSize == otherStats.externalCodeSize
&& externalDataSize == otherStats.externalDataSize
&& externalCacheSize == otherStats.externalCacheSize
@@ -208,7 +244,8 @@
@Override
public int hashCode() {
return Objects.hash(packageName, userHandle, codeSize, dataSize,
- cacheSize, externalCodeSize, externalDataSize, externalCacheSize, externalMediaSize,
+ apkSize, libSize, dmSize, cacheSize, externalCodeSize,
+ externalDataSize, externalCacheSize, externalMediaSize,
externalObbSize);
}
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
index 8c21974..bb09ad2 100644
--- a/core/java/android/content/pm/SigningDetails.java
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -31,6 +31,8 @@
import libcore.util.HexEncoding;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.util.ArrayList;
@@ -49,6 +51,7 @@
private static final String TAG = "SigningDetails";
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({SignatureSchemeVersion.UNKNOWN,
SignatureSchemeVersion.JAR,
SignatureSchemeVersion.SIGNING_BLOCK_V2,
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index a407704..23daaf2 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -17,9 +17,9 @@
package android.content.pm;
import android.annotation.FlaggedApi;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -53,7 +53,7 @@
* schemas</a>
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
- public SigningInfo(@IntRange(from = 0) int schemeVersion,
+ public SigningInfo(@SignatureSchemeVersion int schemeVersion,
@Nullable Collection<Signature> apkContentsSigners,
@Nullable Collection<PublicKey> publicKeys,
@Nullable Collection<Signature> signingCertificateHistory) {
@@ -168,7 +168,7 @@
* schemas</a>
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
- public @IntRange(from = 0) int getSchemeVersion() {
+ public @SignatureSchemeVersion int getSchemeVersion() {
return mSigningDetails.getSignatureSchemeVersion();
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index b04b7ba..94bec35 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -115,3 +115,19 @@
description: "Feature flag to fix duplicated PackageManager flag values"
bug: "314815969"
}
+
+flag {
+ name: "provide_info_of_apk_in_apex"
+ namespace: "package_manager_service"
+ description: "Feature flag to provide the information of APK-in-APEX"
+ bug: "306329516"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "improve_home_app_behavior"
+ namespace: "package_manager_service"
+ description: "Feature flag to improve the uninstallation and preferred activity of home app."
+ bug: "310801107"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 9a1796f..c7797c7 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -64,3 +64,10 @@
bug: "296829976"
is_fixed_read_only: true
}
+
+flag {
+ name: "allow_resolver_sheet_for_private_space"
+ namespace: "profile_experiences"
+ description: "Add support for Private Space in resolver sheet"
+ bug: "307515485"
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 40e03db..60ad8e8 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -86,6 +86,8 @@
private static native long nativeCreate(String opPackageName);
private static native boolean nativeGetSensorAtIndex(long nativeInstance,
Sensor sensor, int index);
+ private static native boolean nativeGetDefaultDeviceSensorAtIndex(long nativeInstance,
+ Sensor sensor, int index);
private static native void nativeGetDynamicSensors(long nativeInstance, List<Sensor> list);
private static native void nativeGetRuntimeSensors(
long nativeInstance, int deviceId, List<Sensor> list);
@@ -162,11 +164,14 @@
// initialize the sensor list
for (int index = 0;; ++index) {
Sensor sensor = new Sensor();
- if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+ if (android.companion.virtual.flags.Flags.enableNativeVdm()) {
+ if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break;
+ } else {
+ if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+ }
mFullSensorsList.add(sensor);
mHandleToSensor.put(sensor.getHandle(), sensor);
}
-
}
/** @hide */
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 3ab889d..665d8d2 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -557,13 +557,15 @@
* on a particular SessionConfiguration.</p>
*
* @return List of CameraCharacteristic keys containing characterisitics specific to a session
- * configuration. For Android 15, this list only contains CONTROL_ZOOM_RATIO_RANGE.
+ * configuration. For Android 15, this list only contains CONTROL_ZOOM_RATIO_RANGE and
+ * SCALER_AVAILABLE_MAX_DIGITAL_ZOOM.
*/
@NonNull
@FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
public List<CameraCharacteristics.Key<?>> getAvailableSessionCharacteristicsKeys() {
if (mAvailableSessionCharacteristicsKeys == null) {
- mAvailableSessionCharacteristicsKeys = Arrays.asList(CONTROL_ZOOM_RATIO_RANGE);
+ mAvailableSessionCharacteristicsKeys =
+ Arrays.asList(CONTROL_ZOOM_RATIO_RANGE, SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
}
return mAvailableSessionCharacteristicsKeys;
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index f71e853..64a62a9 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -715,6 +715,14 @@
boolean startOffload();
void stopOffload();
+
+ /**
+ * Called when {@link DisplayOffloadSession} tries to block screen turning on.
+ *
+ * @param unblocker a {@link Runnable} executed upon all work required before screen turning
+ * on is done.
+ */
+ void onBlockingScreenOn(Runnable unblocker);
}
/** A session token that associates a internal display with a {@link DisplayOffloader}. */
@@ -734,6 +742,32 @@
*/
void updateBrightness(float brightness);
+ /**
+ * Called while display is turning to state ON to leave a small period for displayoffload
+ * session to finish some work.
+ *
+ * @param unblocker a {@link Runnable} used by displayoffload session to notify
+ * {@link DisplayManager} that it can continue turning screen on.
+ */
+ boolean blockScreenOn(Runnable unblocker);
+
+ /**
+ * Get the brightness levels used to determine automatic brightness based on lux levels.
+ * @param mode The auto-brightness mode
+ * (AutomaticBrightnessController.AutomaticBrightnessMode)
+ * @return The brightness levels for the specified mode. The values are between
+ * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
+ */
+ float[] getAutoBrightnessLevels(int mode);
+
+ /**
+ * Get the lux levels used to determine automatic brightness.
+ * @param mode The auto-brightness mode
+ * (AutomaticBrightnessController.AutomaticBrightnessMode)
+ * @return The lux levels for the specified mode
+ */
+ float[] getAutoBrightnessLuxLevels(int mode);
+
/** Returns whether displayoffload supports the given display state. */
static boolean isSupportedOffloadState(int displayState) {
return Display.isSuspendedState(displayState);
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index cb9c333..d939532 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -18,6 +18,7 @@
import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
+import static com.android.input.flags.Flags.enableInputFilterRustImpl;
import android.Manifest;
import android.annotation.FloatRange;
@@ -338,6 +339,21 @@
}
/**
+ * Whether Accessibility bounce keys feature is enabled.
+ *
+ * <p>
+ * Bounce keys’ is an accessibility feature to aid users who have physical disabilities,
+ * that allows the user to configure the device to ignore rapid, repeated keypresses of the
+ * same key.
+ * </p>
+ *
+ * @hide
+ */
+ public static boolean isAccessibilityBounceKeysFeatureEnabled() {
+ return keyboardA11yBounceKeysFlag() && enableInputFilterRustImpl();
+ }
+
+ /**
* Whether Accessibility bounce keys is enabled.
*
* <p>
@@ -364,10 +380,10 @@
* @hide
*/
public static int getAccessibilityBounceKeysThreshold(@NonNull Context context) {
- if (!keyboardA11yBounceKeysFlag()) {
+ if (!isAccessibilityBounceKeysFeatureEnabled()) {
return 0;
}
- return Settings.System.getIntForUser(context.getContentResolver(),
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS, 0, UserHandle.USER_CURRENT);
}
@@ -388,7 +404,7 @@
@RequiresPermission(Manifest.permission.WRITE_SETTINGS)
public static void setAccessibilityBounceKeysThreshold(@NonNull Context context,
int thresholdTimeMillis) {
- if (!keyboardA11yBounceKeysFlag()) {
+ if (!isAccessibilityBounceKeysFeatureEnabled()) {
return;
}
if (thresholdTimeMillis < 0
@@ -397,12 +413,29 @@
"Provided Bounce keys threshold should be in range [0, "
+ MAX_ACCESSIBILITY_BOUNCE_KEYS_THRESHOLD_MILLIS + "]");
}
- Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.Secure.putIntForUser(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS, thresholdTimeMillis,
UserHandle.USER_CURRENT);
}
/**
+ * Whether Accessibility sticky keys feature is enabled.
+ *
+ * <p>
+ * 'Sticky keys' is an accessibility feature that assists users who have physical
+ * disabilities or help users reduce repetitive strain injury. It serializes keystrokes
+ * instead of pressing multiple keys at a time, allowing the user to press and release a
+ * modifier key, such as Shift, Ctrl, Alt, or any other modifier key, and have it remain
+ * active until any other key is pressed.
+ * </p>
+ *
+ * @hide
+ */
+ public static boolean isAccessibilityStickyKeysFeatureEnabled() {
+ return keyboardA11yStickyKeysFlag() && enableInputFilterRustImpl();
+ }
+
+ /**
* Whether Accessibility sticky keys is enabled.
*
* <p>
@@ -416,10 +449,10 @@
* @hide
*/
public static boolean isAccessibilityStickyKeysEnabled(@NonNull Context context) {
- if (!keyboardA11yStickyKeysFlag()) {
+ if (!isAccessibilityStickyKeysFeatureEnabled()) {
return false;
}
- return Settings.System.getIntForUser(context.getContentResolver(),
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_STICKY_KEYS, 0, UserHandle.USER_CURRENT) != 0;
}
@@ -439,10 +472,10 @@
@RequiresPermission(Manifest.permission.WRITE_SETTINGS)
public static void setAccessibilityStickyKeysEnabled(@NonNull Context context,
boolean enabled) {
- if (!keyboardA11yStickyKeysFlag()) {
+ if (!isAccessibilityStickyKeysFeatureEnabled()) {
return;
}
- Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.Secure.putIntForUser(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_STICKY_KEYS, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
}
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index b69410c..a86396c 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -196,7 +196,8 @@
}
/**
- * Retrieves the input stream for this instance.
+ * Retrieves the input stream for this instance. Closing this stream is equivalent to closing
+ * the entire socket and its associated streams using {@link #close()}.
*
* @return input stream
* @throws IOException if socket has been closed or cannot be created.
@@ -207,7 +208,8 @@
}
/**
- * Retrieves the output stream for this instance.
+ * Retrieves the output stream for this instance. Closing this stream is equivalent to closing
+ * the entire socket and its associated streams using {@link #close()}.
*
* @return output stream
* @throws IOException if socket has been closed or cannot be created.
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index f6beec1..967a0cc 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -30,8 +30,10 @@
import android.nfc.INfcUnlockHandler;
import android.nfc.ITagRemovedCallback;
import android.nfc.INfcDta;
+import android.nfc.INfcWlcStateListener;
import android.nfc.NfcAntennaInfo;
import android.os.Bundle;
+import android.nfc.WlcLDeviceInfo;
/**
* @hide
@@ -86,4 +88,11 @@
boolean enableReaderOption(boolean enable);
boolean isObserveModeSupported();
boolean setObserveMode(boolean enabled);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+ boolean enableWlc(boolean enable);
+ boolean isWlcEnabled();
+ void registerWlcStateListener(in INfcWlcStateListener listener);
+ void unregisterWlcStateListener(in INfcWlcStateListener listener);
+ WlcLDeviceInfo getWlcLDeviceInfo();
}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl b/core/java/android/nfc/INfcWlcStateListener.aidl
similarity index 63%
copy from core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
copy to core/java/android/nfc/INfcWlcStateListener.aidl
index 6c1f0fc..c2b7075 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
+++ b/core/java/android/nfc/INfcWlcStateListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -14,11 +14,17 @@
* limitations under the License.
*/
-package android.companion.virtual.camera;
+package android.nfc;
+import android.nfc.WlcLDeviceInfo;
/**
- * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
- * VirtualCamera.
* @hide
*/
-parcelable VirtualCameraMetadata;
+oneway interface INfcWlcStateListener {
+ /**
+ * Called whenever NFC WLC state changes
+ *
+ * @param wlcLDeviceInfo NFC wlc listener information
+ */
+ void onWlcStateChanged(in WlcLDeviceInfo wlcLDeviceInfo);
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index f407fb7..21e23ae 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -75,6 +75,7 @@
static final String TAG = "NFC";
private final NfcControllerAlwaysOnListener mControllerAlwaysOnListener;
+ private final NfcWlcStateListener mNfcWlcStateListener;
/**
* Intent to start an activity when a tag with NDEF payload is discovered.
@@ -440,6 +441,7 @@
static boolean sIsInitialized = false;
static boolean sHasNfcFeature;
static boolean sHasCeFeature;
+ static boolean sHasNfcWlcFeature;
// Final after first constructor, except for
// attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -650,8 +652,9 @@
|| pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)
|| pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)
|| pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE);
+ sHasNfcWlcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_CHARGING);
/* is this device meant to have NFC */
- if (!sHasNfcFeature && !sHasCeFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature && !sHasNfcWlcFeature) {
Log.v(TAG, "this device does not have NFC support");
throw new UnsupportedOperationException();
}
@@ -776,6 +779,7 @@
mTagRemovedListener = null;
mLock = new Object();
mControllerAlwaysOnListener = new NfcControllerAlwaysOnListener(getService());
+ mNfcWlcStateListener = new NfcWlcStateListener(getService());
}
/**
@@ -944,7 +948,8 @@
Log.e(TAG, "Failed to recover NFC Service.");
}
}
- return serviceState && (isTagReadingEnabled() || isCardEmulationEnabled());
+ return serviceState
+ && (isTagReadingEnabled() || isCardEmulationEnabled() || sHasNfcWlcFeature);
}
/**
@@ -2587,4 +2592,159 @@
return false;
}
}
+
+ /**
+ * Sets NFC charging feature.
+ * <p>This API is for the Settings application.
+ * @return True if successful
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public boolean enableWlc(boolean enable) {
+ if (!sHasNfcWlcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.enableWlc(enable);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return false;
+ }
+ try {
+ return sService.enableWlc(enable);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Checks NFC charging feature is enabled.
+ *
+ * @return True if NFC charging is enabled, false otherwise
+ * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+ * is unavailable
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+ public boolean isWlcEnabled() {
+ if (!sHasNfcWlcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.isWlcEnabled();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return false;
+ }
+ try {
+ return sService.isWlcEnabled();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ return false;
+ }
+ }
+
+ /**
+ * A listener to be invoked when NFC controller always on state changes.
+ * <p>Register your {@code ControllerAlwaysOnListener} implementation with {@link
+ * NfcAdapter#registerWlcStateListener} and disable it with {@link
+ * NfcAdapter#unregisterWlcStateListenerListener}.
+ * @see #registerWlcStateListener
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+ public interface WlcStateListener {
+ /**
+ * Called on NFC WLC state changes
+ */
+ void onWlcStateChanged(@NonNull WlcLDeviceInfo wlcLDeviceInfo);
+ }
+
+ /**
+ * Register a {@link WlcStateListener} to listen for NFC WLC state changes
+ * <p>The provided listener will be invoked by the given {@link Executor}.
+ *
+ * @param executor an {@link Executor} to execute given listener
+ * @param listener user implementation of the {@link WlcStateListener}
+ * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+ * is unavailable
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+ public void registerWlcStateListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull WlcStateListener listener) {
+ if (!sHasNfcWlcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ mNfcWlcStateListener.register(executor, listener);
+ }
+
+ /**
+ * Unregister the specified {@link WlcStateListener}
+ * <p>The same {@link WlcStateListener} object used when calling
+ * {@link #registerWlcStateListener(Executor, WlcStateListener)}
+ * must be used.
+ *
+ * <p>Listeners are automatically unregistered when application process goes away
+ *
+ * @param listener user implementation of the {@link WlcStateListener}a
+ * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+ * is unavailable
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+ public void unregisterWlcStateListener(
+ @NonNull WlcStateListener listener) {
+ if (!sHasNfcWlcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ mNfcWlcStateListener.unregister(listener);
+ }
+
+ /**
+ * Returns information on the NFC charging listener device
+ *
+ * @return Information on the NFC charging listener device
+ * @throws UnsupportedOperationException if FEATURE_NFC_CHARGING
+ * is unavailable
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+ @Nullable
+ public WlcLDeviceInfo getWlcLDeviceInfo() {
+ if (!sHasNfcWlcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.getWlcLDeviceInfo();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return null;
+ }
+ try {
+ return sService.getWlcLDeviceInfo();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ return null;
+ }
+ }
}
diff --git a/core/java/android/nfc/NfcWlcStateListener.java b/core/java/android/nfc/NfcWlcStateListener.java
new file mode 100644
index 0000000..8d79310
--- /dev/null
+++ b/core/java/android/nfc/NfcWlcStateListener.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 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.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.WlcStateListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcWlcStateListener extends INfcWlcStateListener.Stub {
+ private static final String TAG = NfcWlcStateListener.class.getSimpleName();
+
+ private final INfcAdapter mAdapter;
+
+ private final Map<WlcStateListener, Executor> mListenerMap = new HashMap<>();
+
+ private WlcLDeviceInfo mCurrentState = null;
+ private boolean mIsRegistered = false;
+
+ public NfcWlcStateListener(@NonNull INfcAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /**
+ * Register a {@link WlcStateListener} with this
+ * {@link WlcStateListener}
+ *
+ * @param executor an {@link Executor} to execute given listener
+ * @param listener user implementation of the {@link WlcStateListener}
+ */
+ public void register(@NonNull Executor executor, @NonNull WlcStateListener listener) {
+ synchronized (this) {
+ if (mListenerMap.containsKey(listener)) {
+ return;
+ }
+
+ mListenerMap.put(listener, executor);
+
+ if (!mIsRegistered) {
+ try {
+ mAdapter.registerWlcStateListener(this);
+ mIsRegistered = true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to register");
+ }
+ }
+ }
+ }
+
+ /**
+ * Unregister the specified {@link WlcStateListener}
+ *
+ * @param listener user implementation of the {@link WlcStateListener}
+ */
+ public void unregister(@NonNull WlcStateListener listener) {
+ synchronized (this) {
+ if (!mListenerMap.containsKey(listener)) {
+ return;
+ }
+
+ mListenerMap.remove(listener);
+
+ if (mListenerMap.isEmpty() && mIsRegistered) {
+ try {
+ mAdapter.unregisterWlcStateListener(this);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to unregister");
+ }
+ mIsRegistered = false;
+ }
+ }
+ }
+
+ private void sendCurrentState(@NonNull WlcStateListener listener) {
+ synchronized (this) {
+ Executor executor = mListenerMap.get(listener);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onWlcStateChanged(
+ mCurrentState));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onWlcStateChanged(@NonNull WlcLDeviceInfo wlcLDeviceInfo) {
+ synchronized (this) {
+ mCurrentState = wlcLDeviceInfo;
+
+ for (WlcStateListener cb : mListenerMap.keySet()) {
+ sendCurrentState(cb);
+ }
+ }
+ }
+}
+
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl b/core/java/android/nfc/WlcLDeviceInfo.aidl
similarity index 74%
copy from core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
copy to core/java/android/nfc/WlcLDeviceInfo.aidl
index 6c1f0fc..33143fe 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
+++ b/core/java/android/nfc/WlcLDeviceInfo.aidl
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-package android.companion.virtual.camera;
+package android.nfc;
-/**
- * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
- * VirtualCamera.
- * @hide
- */
-parcelable VirtualCameraMetadata;
+parcelable WlcLDeviceInfo;
diff --git a/core/java/android/nfc/WlcLDeviceInfo.java b/core/java/android/nfc/WlcLDeviceInfo.java
new file mode 100644
index 0000000..016431e
--- /dev/null
+++ b/core/java/android/nfc/WlcLDeviceInfo.java
@@ -0,0 +1,107 @@
+/*
+ * 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.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains information of the nfc wireless charging listener device information.
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_NFC_CHARGING)
+public final class WlcLDeviceInfo implements Parcelable {
+ public static final int DISCONNECTED = 1;
+
+ public static final int CONNECTED_CHARGING = 2;
+
+ public static final int CONNECTED_DISCHARGING = 3;
+
+ private double mProductId;
+ private double mTemperature;
+ private double mBatteryLevel;
+ private int mState;
+
+ public WlcLDeviceInfo(double productId, double temperature, double batteryLevel, int state) {
+ this.mProductId = productId;
+ this.mTemperature = temperature;
+ this.mBatteryLevel = batteryLevel;
+ this.mState = state;
+ }
+
+ /**
+ * ProductId of the WLC listener device.
+ */
+ public double getProductId() {
+ return mProductId;
+ }
+
+ /**
+ * Temperature of the WLC listener device.
+ */
+ public double getTemperature() {
+ return mTemperature;
+ }
+
+ /**
+ * BatteryLevel of the WLC listener device.
+ */
+ public double getBatteryLevel() {
+ return mBatteryLevel;
+ }
+
+ /**
+ * State of the WLC listener device.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ private WlcLDeviceInfo(Parcel in) {
+ this.mProductId = in.readDouble();
+ this.mTemperature = in.readDouble();
+ this.mBatteryLevel = in.readDouble();
+ this.mState = in.readInt();
+ }
+
+ public static final @NonNull Parcelable.Creator<WlcLDeviceInfo> CREATOR =
+ new Parcelable.Creator<WlcLDeviceInfo>() {
+ @Override
+ public WlcLDeviceInfo createFromParcel(Parcel in) {
+ return new WlcLDeviceInfo(in);
+ }
+
+ @Override
+ public WlcLDeviceInfo[] newArray(int size) {
+ return new WlcLDeviceInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mProductId);
+ dest.writeDouble(mTemperature);
+ dest.writeDouble(mBatteryLevel);
+ dest.writeDouble(mState);
+ }
+}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index bd087f9..41dee3a 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -21,10 +21,10 @@
package android.nfc.cardemulation;
import android.annotation.FlaggedApi;
-import android.compat.annotation.UnsupportedAppUsage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -374,7 +374,7 @@
// Set uid
mUid = si.applicationInfo.uid;
- mCategoryOtherServiceEnabled = false; // support other category
+ mCategoryOtherServiceEnabled = true; // support other category
}
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index 17e0427..ce4f777 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -48,3 +48,17 @@
description: "Enable NFC Polling Loop Notifications ST shim"
bug: "294217286"
}
+
+flag {
+ name: "enable_tag_detection_broadcasts"
+ namespace: "nfc"
+ description: "Enable sending broadcasts to Wallet role holder when a tag enters/leaves the field."
+ bug: "306203494"
+}
+
+flag {
+ name: "enable_nfc_charging"
+ namespace: "nfc"
+ description: "Flag for NFC charging changes"
+ bug: "292143899"
+}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 209a595..d3f2c7a 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -92,3 +92,6 @@
per-file IThermal* = file:/THERMAL_OWNERS
per-file CoolingDevice.java = file:/THERMAL_OWNERS
per-file Temperature.java = file:/THERMAL_OWNERS
+
+# SecurityStateManager
+per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS
\ No newline at end of file
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index fc8523e..f952fcf 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -21,6 +21,7 @@
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UptimeMillisLong;
@@ -757,7 +758,6 @@
@Nullable String invokeWith,
@Nullable String packageName,
@Nullable long[] disabledCompatChanges,
- boolean bindMountSyspropOverrides,
@Nullable String[] zygoteArgs) {
// Webview zygote can't access app private data files, so doesn't need to know its data
// info.
@@ -767,7 +767,7 @@
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
disabledCompatChanges, /* pkgDataInfoMap */ null,
/* whitelistedDataInfoMap */ null, /* bindMountAppsData */ false,
- /* bindMountAppStorageDirs */ false, bindMountSyspropOverrides, zygoteArgs);
+ /* bindMountAppStorageDirs */ false, /* bindMountSyspropOverrides */ false, zygoteArgs);
}
/**
@@ -979,12 +979,10 @@
}
/**
- * Returns whether the provided UID belongs to a SDK sandbox process.
- *
- * @hide
+ * Returns whether the provided UID belongs to an sdk sandbox process
+ * @see android.app.sdksandbox.SdkSandboxManager
*/
- @SystemApi(client = MODULE_LIBRARIES)
- @TestApi
+ @SuppressLint("UnflaggedApi") // promoting from @SystemApi.
@android.ravenwood.annotation.RavenwoodKeep
public static final boolean isSdkSandboxUid(int uid) {
uid = UserHandle.getAppId(uid);
@@ -992,15 +990,20 @@
}
/**
+ * Returns the app uid corresponding to an sdk sandbox uid.
+ * @see android.app.sdksandbox.SdkSandboxManager
*
- * Returns the app process corresponding to an sdk sandbox process.
+ * @param uid the sdk sandbox uid
+ * @return the app uid for the given sdk sandbox uid
*
- * @hide
+ * @throws IllegalArgumentException if input is not an sdk sandbox uid
*/
- @SystemApi(client = MODULE_LIBRARIES)
- @TestApi
+ @SuppressLint("UnflaggedApi") // promoting from @SystemApi.
@android.ravenwood.annotation.RavenwoodKeep
public static final int getAppUidForSdkSandboxUid(int uid) {
+ if (!isSdkSandboxUid(uid)) {
+ throw new IllegalArgumentException("Input UID is not an SDK sandbox UID");
+ }
return uid - (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
diff --git a/core/java/android/os/ServiceSpecificException.java b/core/java/android/os/ServiceSpecificException.java
index 49ce40b..df503e8 100644
--- a/core/java/android/os/ServiceSpecificException.java
+++ b/core/java/android/os/ServiceSpecificException.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
/**
* An exception specific to a service.
@@ -33,6 +34,7 @@
* @hide
*/
@SystemApi
+@RavenwoodKeepWholeClass
public class ServiceSpecificException extends RuntimeException {
public final int errorCode;
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 83d237d..d646146 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -91,3 +91,10 @@
is_fixed_read_only: true
bug: "315037695"
}
+
+flag {
+ name: "strict_mode_restricted_network"
+ namespace: "backstage_power"
+ description: "Guards StrictMode APIs for detecting restricted network access."
+ bug: "317250784"
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 4bfff16..7d00b80 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6911,7 +6911,11 @@
* <td></td>
* </tr>
* </table>
+ *
+ * @deprecated This field may not be well supported by some contacts apps and is discouraged
+ * to use.
*/
+ @Deprecated
public static final class Im implements DataColumnsWithJoins, CommonColumns, ContactCounts {
/**
* This utility class cannot be instantiated
@@ -7721,7 +7725,11 @@
* <td></td>
* </tr>
* </table>
+ *
+ * @deprecated This field may not be well supported by some contacts apps and is discouraged
+ * to use.
*/
+ @Deprecated
public static final class SipAddress implements DataColumnsWithJoins, CommonColumns,
ContactCounts {
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4af657d..54cc5f4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14339,6 +14339,14 @@
public static final String MODE_RINGER = "mode_ringer";
/**
+ * Whether or not Alarm stream should always be muted with Ringer.
+ *
+ * @hide
+ */
+ public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE =
+ "mute_alarm_stream_with_ringer_mode";
+
+ /**
* Overlay display devices setting.
* The associated value is a specially formatted string that describes the
* size and density of simulated secondary display devices.
@@ -15013,6 +15021,16 @@
"foreground_service_starts_logging_enabled";
/**
+ * Describes whether AM's AppProfiler should collect PSS even if RSS is the default. This
+ * can be set by a user in developer settings.
+ * Default: 0
+ * @hide
+ */
+ @Readable
+ public static final String FORCE_ENABLE_PSS_PROFILING =
+ "force_enable_pss_profiling";
+
+ /**
* @hide
* @see com.android.server.appbinding.AppBindingConstants
*/
@@ -19620,6 +19638,15 @@
*/
public static final String WEAR_POWER_ANOMALY_SERVICE_ENABLED =
"wear_power_anomaly_service_enabled";
+
+ /**
+ * A boolean that tracks whether Wrist Detection Auto-Locking is enabled.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String WRIST_DETECTION_AUTO_LOCKING_ENABLED =
+ "wear_wrist_detection_auto_locking_enabled";
}
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 72c436e..e4af2da 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4940,6 +4940,13 @@
*/
public static final String COLUMN_IS_NTN = "is_ntn";
+ /**
+ * TelephonyProvider column name to indicate the service capability bitmasks.
+ *
+ * @hide
+ */
+ public static final String COLUMN_SERVICE_CAPABILITIES = "service_capabilities";
+
/** All columns in {@link SimInfo} table. */
private static final List<String> ALL_COLUMNS = List.of(
COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
@@ -5011,7 +5018,8 @@
COLUMN_USER_HANDLE,
COLUMN_SATELLITE_ENABLED,
COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
- COLUMN_IS_NTN
+ COLUMN_IS_NTN,
+ COLUMN_SERVICE_CAPABILITIES
);
/**
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index b56bef3..30524a1 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -50,3 +50,11 @@
description: "Collect sepolicy hash from sysfs"
bug: "308471499"
}
+
+flag {
+ name: "frp_enforcement"
+ namespace: "android_hw_security"
+ description: "This flag controls whether PDB enforces FRP"
+ bug: "290312729"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 78248d9..e1965ef 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -53,7 +53,6 @@
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IContentCaptureDirectManager;
-import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.FrameworkStatsLog;
@@ -724,7 +723,7 @@
final Bundle extras;
if (binder != null) {
extras = new Bundle();
- extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder);
+ extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
} else {
extras = null;
}
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index b1680ab..3c1a279 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -256,7 +256,7 @@
* @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
* {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
*/
- public @PeopleType int getPriorityConversationSenders() {
+ public @ConversationSenders int getPriorityConversationSenders() {
return mConversationSenders;
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 875031f..23c8393 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -960,8 +960,8 @@
mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales,
AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER);
}
+ notifyStateChangedLocked();
}
- notifyStateChanged(availability);
}
/**
@@ -1371,8 +1371,8 @@
mAvailability = STATE_INVALID;
mIsAvailabilityOverriddenByTestApi = false;
+ notifyStateChangedLocked();
}
- notifyStateChanged(STATE_INVALID);
super.destroy();
}
@@ -1402,8 +1402,6 @@
*/
// TODO(b/281608561): remove the enrollment flow from AlwaysOnHotwordDetector
void onSoundModelsChanged() {
- boolean notifyError = false;
-
synchronized (mLock) {
if (mAvailability == STATE_INVALID
|| mAvailability == STATE_HARDWARE_UNAVAILABLE
@@ -1444,9 +1442,6 @@
// calling stopRecognition where there is no started session.
Log.w(TAG, "Failed to stop recognition after enrollment update: code="
+ result);
-
- // Execute a refresh availability task - which should then notify of a change.
- new RefreshAvailabilityTask().execute();
} catch (Exception e) {
Slog.w(TAG, "Failed to stop recognition after enrollment update", e);
if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
@@ -1455,14 +1450,14 @@
+ Log.getStackTraceString(e),
FailureSuggestedAction.RECREATE_DETECTOR));
} else {
- notifyError = true;
+ updateAndNotifyStateChangedLocked(STATE_ERROR);
}
+ return;
}
}
- }
- if (notifyError) {
- updateAndNotifyStateChanged(STATE_ERROR);
+ // Execute a refresh availability task - which should then notify of a change.
+ new RefreshAvailabilityTask().execute();
}
}
@@ -1578,11 +1573,10 @@
}
}
- private void updateAndNotifyStateChanged(int availability) {
- synchronized (mLock) {
- updateAvailabilityLocked(availability);
- }
- notifyStateChanged(availability);
+ @GuardedBy("mLock")
+ private void updateAndNotifyStateChangedLocked(int availability) {
+ updateAvailabilityLocked(availability);
+ notifyStateChangedLocked();
}
@GuardedBy("mLock")
@@ -1596,17 +1590,17 @@
}
}
- private void notifyStateChanged(int newAvailability) {
+ @GuardedBy("mLock")
+ private void notifyStateChangedLocked() {
Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
- message.arg1 = newAvailability;
+ message.arg1 = mAvailability;
message.sendToTarget();
}
+ @GuardedBy("mLock")
private void sendUnknownFailure(String failureMessage) {
- synchronized (mLock) {
- // update but do not call onAvailabilityChanged callback for STATE_ERROR
- updateAvailabilityLocked(STATE_ERROR);
- }
+ // update but do not call onAvailabilityChanged callback for STATE_ERROR
+ updateAvailabilityLocked(STATE_ERROR);
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE, failureMessage).sendToTarget();
}
@@ -1822,17 +1816,19 @@
availability = STATE_KEYPHRASE_UNENROLLED;
}
}
+ updateAndNotifyStateChangedLocked(availability);
}
- updateAndNotifyStateChanged(availability);
} catch (Exception e) {
// Any exception here not caught will crash the process because AsyncTask does not
// bubble up the exceptions to the client app, so we must propagate it to the app.
Slog.w(TAG, "Failed to refresh availability", e);
- if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
- sendUnknownFailure(
- "Failed to refresh availability: " + Log.getStackTraceString(e));
- } else {
- updateAndNotifyStateChanged(STATE_ERROR);
+ synchronized (mLock) {
+ if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
+ sendUnknownFailure(
+ "Failed to refresh availability: " + Log.getStackTraceString(e));
+ } else {
+ updateAndNotifyStateChangedLocked(STATE_ERROR);
+ }
}
}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index b7d9705..adc54f5 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -94,7 +94,9 @@
*/
public void updateState(@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory) {
- mInitializationDelegate.updateState(options, sharedMemory);
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.updateState(options, sharedMemory);
+ }
}
@@ -116,18 +118,21 @@
if (DEBUG) {
Slog.i(TAG, "#startRecognition");
}
- // check if the detector is active with the initialization delegate
- mInitializationDelegate.startRecognition();
+ synchronized (mInitializationDelegate.getLock()) {
+ // check if the detector is active with the initialization delegate
+ mInitializationDelegate.startRecognition();
- try {
- mManagerService.startPerceiving(new BinderCallback(mExecutor, mCallback));
- } catch (SecurityException e) {
- Slog.e(TAG, "startRecognition failed: " + e);
- return false;
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ try {
+ mManagerService.startPerceiving(new BinderCallback(
+ mExecutor, mCallback, mInitializationDelegate.getLock()));
+ } catch (SecurityException e) {
+ Slog.e(TAG, "startRecognition failed: " + e);
+ return false;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return true;
}
- return true;
}
/**
@@ -140,15 +145,17 @@
if (DEBUG) {
Slog.i(TAG, "#stopRecognition");
}
- // check if the detector is active with the initialization delegate
- mInitializationDelegate.startRecognition();
+ synchronized (mInitializationDelegate.getLock()) {
+ // check if the detector is active with the initialization delegate
+ mInitializationDelegate.stopRecognition();
- try {
- mManagerService.stopPerceiving();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ try {
+ mManagerService.stopPerceiving();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return true;
}
- return true;
}
/**
@@ -160,12 +167,16 @@
if (DEBUG) {
Slog.i(TAG, "#destroy");
}
- mInitializationDelegate.destroy();
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.destroy();
+ }
}
/** @hide */
public void dump(String prefix, PrintWriter pw) {
- // TODO: implement this
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.dump(prefix, pw);
+ }
}
/** @hide */
@@ -175,7 +186,9 @@
/** @hide */
void registerOnDestroyListener(Consumer<AbstractDetector> onDestroyListener) {
- mInitializationDelegate.registerOnDestroyListener(onDestroyListener);
+ synchronized (mInitializationDelegate.getLock()) {
+ mInitializationDelegate.registerOnDestroyListener(onDestroyListener);
+ }
}
/**
@@ -282,6 +295,15 @@
public boolean isUsingSandboxedDetectionService() {
return true;
}
+
+ @Override
+ public void dump(String prefix, PrintWriter pw) {
+ // No-op
+ }
+
+ private Object getLock() {
+ return mLock;
+ }
}
private static class BinderCallback
@@ -289,31 +311,43 @@
private final Executor mExecutor;
private final VisualQueryDetector.Callback mCallback;
- BinderCallback(Executor executor, VisualQueryDetector.Callback callback) {
+ private final Object mLock;
+
+ BinderCallback(Executor executor, VisualQueryDetector.Callback callback, Object lock) {
this.mExecutor = executor;
this.mCallback = callback;
+ this.mLock = lock;
}
/** Called when the detected result is valid. */
@Override
public void onQueryDetected(@NonNull String partialQuery) {
Slog.v(TAG, "BinderCallback#onQueryDetected");
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> mCallback.onQueryDetected(partialQuery)));
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mCallback.onQueryDetected(partialQuery);
+ }
+ });
}
@Override
public void onQueryFinished() {
Slog.v(TAG, "BinderCallback#onQueryFinished");
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> mCallback.onQueryFinished()));
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mCallback.onQueryFinished();
+ }
+ });
}
@Override
public void onQueryRejected() {
Slog.v(TAG, "BinderCallback#onQueryRejected");
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> mCallback.onQueryRejected()));
+ Binder.withCleanCallingIdentity(() -> {
+ synchronized (mLock) {
+ mCallback.onQueryRejected();
+ }
+ });
}
/** Called when the detection fails due to an error. */
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 712d1d6..f819c9b 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.content.res.Resources.ID_NULL;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static android.view.DisplayCutoutProto.BOUND_BOTTOM;
@@ -23,8 +24,12 @@
import static android.view.DisplayCutoutProto.BOUND_RIGHT;
import static android.view.DisplayCutoutProto.BOUND_TOP;
import static android.view.DisplayCutoutProto.INSETS;
+import static android.view.DisplayCutoutProto.SIDE_OVERRIDES;
import static android.view.DisplayCutoutProto.WATERFALL_INSETS;
import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
@@ -49,6 +54,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -112,6 +118,9 @@
private static float sCachedPhysicalPixelDisplaySizeRatio;
@GuardedBy("CACHE_LOCK")
+ private static int[] sCachedSideOverrides;
+
+ @GuardedBy("CACHE_LOCK")
private static CutoutPathParserInfo sCachedCutoutPathParserInfo;
@GuardedBy("CACHE_LOCK")
private static Path sCachedCutoutPath;
@@ -150,6 +159,15 @@
*/
public static final int BOUNDS_POSITION_LENGTH = 4;
+ private static final int INVALID_SIDE_OVERRIDE = -1;
+ private static final String SIDE_STRING_TOP = "top";
+ private static final String SIDE_STRING_BOTTOM = "bottom";
+ private static final String SIDE_STRING_RIGHT = "right";
+ private static final String SIDE_STRING_LEFT = "left";
+
+ // The side index is always under the natural rotation of the device.
+ private int[] mSideOverrides;
+
/** @hide */
@IntDef(prefix = { "BOUNDS_POSITION_" }, value = {
BOUNDS_POSITION_LEFT,
@@ -402,8 +420,36 @@
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) {
- this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, null,
- true);
+ this(getCopyOrRef(safeInsets.toRect(), true), Insets.NONE,
+ new Bounds(boundLeft, boundTop, boundRight, boundBottom, true), null, null);
+ }
+
+ /**
+ * Creates a DisplayCutout instance.
+ *
+ * <p>Note that this is only useful for tests. For production code, developers should always
+ * use a {@link DisplayCutout} obtained from the system.</p>
+ *
+ * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+ * {@link #getSafeInsetTop()} etc.
+ * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundTop the top bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundRight the right bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundBottom the bottom bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param waterfallInsets the insets for the curved areas in waterfall display.
+ * @param info the cutout path parser info.
+ * @hide
+ */
+ @VisibleForTesting
+ public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
+ @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
+ @NonNull Insets waterfallInsets, @Nullable CutoutPathParserInfo info) {
+ this(getCopyOrRef(safeInsets.toRect(), true), waterfallInsets,
+ new Bounds(boundLeft, boundTop, boundRight, boundBottom, true), info, null);
}
/**
@@ -428,9 +474,11 @@
*/
public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
- @NonNull Insets waterfallInsets, @Nullable CutoutPathParserInfo info) {
- this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
- info, true);
+ @NonNull Insets waterfallInsets, @Nullable CutoutPathParserInfo info,
+ @Nullable int[] sideOverrides) {
+ this(safeInsets.toRect(), waterfallInsets,
+ new Bounds(boundLeft, boundTop, boundRight, boundBottom, true),
+ info, sideOverrides);
}
/**
@@ -454,8 +502,8 @@
public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
@NonNull Insets waterfallInsets) {
- this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
- null, true);
+ this(getCopyOrRef(safeInsets.toRect(), true), waterfallInsets,
+ new Bounds(boundLeft, boundTop, boundRight, boundBottom, true), null, null);
}
/**
@@ -473,8 +521,8 @@
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
@Deprecated
public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) {
- this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), null,
- true /* copyArguments */);
+ this(getCopyOrRef(safeInsets, true), Insets.NONE,
+ new Bounds(extractBoundsFromList(safeInsets, boundingRects), true), null, null);
}
/**
@@ -498,26 +546,29 @@
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, Rect boundTop,
Rect boundRight, Rect boundBottom, CutoutPathParserInfo info,
boolean copyArguments) {
- mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
- mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
- mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
- mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
+ this(getCopyOrRef(safeInsets, copyArguments), waterfallInsets,
+ new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments), info,
+ null);
}
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds,
CutoutPathParserInfo info, boolean copyArguments) {
- mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
- mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
- mBounds = new Bounds(bounds, copyArguments);
- mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
+ this(getCopyOrRef(safeInsets, copyArguments), waterfallInsets,
+ new Bounds(bounds, copyArguments), info, null);
}
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds,
CutoutPathParserInfo info) {
+ this(safeInsets, waterfallInsets, bounds, info, null);
+ }
+
+ private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds,
+ CutoutPathParserInfo info, int[] sideOverrides) {
mSafeInsets = safeInsets;
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = bounds;
mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
+ mSideOverrides = sideOverrides;
}
private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
@@ -795,6 +846,7 @@
result = 48271 * result + mBounds.hashCode();
result = 48271 * result + mWaterfallInsets.hashCode();
result = 48271 * result + mCutoutPathParserInfo.hashCode();
+ result = 48271 * result + Arrays.hashCode(mSideOverrides);
return result;
}
@@ -807,7 +859,8 @@
DisplayCutout c = (DisplayCutout) o;
return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds)
&& mWaterfallInsets.equals(c.mWaterfallInsets)
- && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo);
+ && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo)
+ && Arrays.equals(mSideOverrides, c.mSideOverrides);
}
return false;
}
@@ -818,9 +871,48 @@
+ " waterfall=" + mWaterfallInsets
+ " boundingRect={" + mBounds + "}"
+ " cutoutPathParserInfo={" + mCutoutPathParserInfo + "}"
+ + " sideOverrides=" + sideOverridesToString(mSideOverrides)
+ "}";
}
+ private static String sideOverridesToString(int[] sideOverrides) {
+ if (sideOverrides == null) {
+ return "null";
+ }
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ final int length = sideOverrides.length;
+ if (length != BOUNDS_POSITION_LENGTH) {
+ sb.append("length=").append(sideOverrides.length).append(". ");
+ }
+ boolean hasContent = false;
+ for (int i = ROTATION_0; i < length; i++) {
+ final int override = sideOverrides[i];
+ if (override != INVALID_SIDE_OVERRIDE) {
+ if (hasContent) {
+ sb.append(", ");
+ }
+ sb.append(Surface.rotationToString(i)).append(": ");
+ switch(override) {
+ case BOUNDS_POSITION_LEFT:
+ sb.append(SIDE_STRING_LEFT);
+ break;
+ case BOUNDS_POSITION_TOP:
+ sb.append(SIDE_STRING_TOP);
+ break;
+ case BOUNDS_POSITION_RIGHT:
+ sb.append(SIDE_STRING_RIGHT);
+ break;
+ case BOUNDS_POSITION_BOTTOM:
+ sb.append(SIDE_STRING_BOTTOM);
+ break;
+ }
+ hasContent = true;
+ }
+ }
+ return sb.append("}").toString();
+ }
+
/**
* @hide
*/
@@ -832,6 +924,11 @@
mBounds.getRect(BOUNDS_POSITION_RIGHT).dumpDebug(proto, BOUND_RIGHT);
mBounds.getRect(BOUNDS_POSITION_BOTTOM).dumpDebug(proto, BOUND_BOTTOM);
mWaterfallInsets.toRect().dumpDebug(proto, WATERFALL_INSETS);
+ if (mSideOverrides != null) {
+ for (int sideOverride : mSideOverrides) {
+ proto.write(SIDE_OVERRIDES, sideOverride);
+ }
+ }
proto.end(token);
}
@@ -899,7 +996,7 @@
*/
public DisplayCutout replaceSafeInsets(Rect safeInsets) {
return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds,
- mCutoutPathParserInfo);
+ mCutoutPathParserInfo, mSideOverrides);
}
private static int atLeastZero(int value) {
@@ -1031,8 +1128,10 @@
Insets insets;
final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
final TypedArray array = res.obtainTypedArray(R.array.config_waterfallCutoutArray);
- if (index >= 0 && index < array.length() && array.getResourceId(index, 0) > 0) {
- final int resourceId = array.getResourceId(index, 0);
+ final int resourceId = index >= 0 && index < array.length()
+ ? array.getResourceId(index, ID_NULL)
+ : ID_NULL;
+ if (resourceId != ID_NULL) {
final TypedArray waterfall = res.obtainTypedArray(resourceId);
insets = Insets.of(
waterfall.getDimensionPixelSize(0 /* waterfall left edge size */, 0),
@@ -1047,6 +1146,48 @@
return insets;
}
+ private static int[] getDisplayCutoutSideOverrides(Resources res, String displayUniqueId)
+ throws IllegalArgumentException {
+ if (!Flags.movableCutoutConfiguration()) {
+ return null;
+ }
+ final int index = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId);
+ final TypedArray array = res.obtainTypedArray(
+ R.array.config_displayCutoutSideOverrideArray);
+ final int resourceId = index >= 0 && index < array.length()
+ ? array.getResourceId(index, ID_NULL)
+ : ID_NULL;
+ final String[] rawOverrides = resourceId != ID_NULL
+ ? array.getResources().getStringArray(resourceId)
+ : res.getStringArray(R.array.config_mainBuiltInDisplayCutoutSideOverride);
+ array.recycle();
+ final int[] override = new int[]{INVALID_SIDE_OVERRIDE, INVALID_SIDE_OVERRIDE,
+ INVALID_SIDE_OVERRIDE, INVALID_SIDE_OVERRIDE};
+ for (String rawOverride : rawOverrides) {
+ int rotation;
+ String[] split = rawOverride.split(" *, *");
+ switch (split[0]) {
+ case "0" -> rotation = ROTATION_0;
+ case "90" -> rotation = ROTATION_90;
+ case "180" -> rotation = ROTATION_180;
+ case "270" -> rotation = ROTATION_270;
+ default -> throw new IllegalArgumentException("Invalid side override definition: "
+ + rawOverride);
+ }
+ int side;
+ switch (split[1]) {
+ case SIDE_STRING_LEFT -> side = BOUNDS_POSITION_LEFT;
+ case SIDE_STRING_TOP -> side = BOUNDS_POSITION_TOP;
+ case SIDE_STRING_RIGHT -> side = BOUNDS_POSITION_RIGHT;
+ case SIDE_STRING_BOTTOM -> side = BOUNDS_POSITION_BOTTOM;
+ default -> throw new IllegalArgumentException("Invalid side override definition: "
+ + rawOverride);
+ }
+ override[rotation] = side;
+ }
+ return override;
+ }
+
/**
* Creates the display cutout according to
* @android:string/config_mainBuiltInDisplayCutoutRectApproximation, which is the closest
@@ -1060,7 +1201,8 @@
getDisplayCutoutApproximationRect(res, displayUniqueId), physicalDisplayWidth,
physicalDisplayHeight, displayWidth, displayHeight,
DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
- getWaterfallInsets(res, displayUniqueId)).second;
+ getWaterfallInsets(res, displayUniqueId),
+ getDisplayCutoutSideOverrides(res, displayUniqueId)).second;
}
/**
@@ -1070,17 +1212,17 @@
*/
@VisibleForTesting(visibility = PRIVATE)
public static DisplayCutout fromSpec(String pathSpec, int displayWidth,
- int displayHeight, float density, Insets waterfallInsets) {
+ int displayHeight, float density, Insets waterfallInsets, int[] sideOverrides) {
return pathAndDisplayCutoutFromSpec(
pathSpec, null, displayWidth, displayHeight, displayWidth, displayHeight, density,
- waterfallInsets).second;
+ waterfallInsets, sideOverrides).second;
}
/**
* Gets the cutout path and the corresponding DisplayCutout instance from the spec string.
*
- * @param pathSpec the spec string read from config_mainBuiltInDisplayCutout.
- * @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation.
+ * @param pathSpec the spec string read from config for certain display.
+ * @param rectSpec the rect approximation spec string read from config for certain display.
* @param physicalDisplayWidth the max physical display width the display supports.
* @param physicalDisplayHeight the max physical display height the display supports.
* @param displayWidth the display width.
@@ -1091,7 +1233,8 @@
*/
private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(
String pathSpec, String rectSpec, int physicalDisplayWidth, int physicalDisplayHeight,
- int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
+ int displayWidth, int displayHeight, float density, Insets waterfallInsets,
+ int[] sideOverrides) {
// Always use the rect approximation spec to create the cutout if it's not null because
// transforming and sending a Region constructed from a path is very costly.
String spec = rectSpec != null ? rectSpec : pathSpec;
@@ -1107,7 +1250,8 @@
&& sCachedDisplayHeight == displayHeight
&& sCachedDensity == density
&& waterfallInsets.equals(sCachedWaterfallInsets)
- && sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio) {
+ && sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio
+ && Arrays.equals(sCachedSideOverrides, sideOverrides)) {
return sCachedCutout;
}
}
@@ -1123,7 +1267,6 @@
final Rect boundRight = cutoutSpec.getRightBound();
final Rect boundBottom = cutoutSpec.getBottomBound();
-
if (!waterfallInsets.equals(Insets.NONE)) {
safeInset.set(
Math.max(waterfallInsets.left, safeInset.left),
@@ -1135,10 +1278,21 @@
final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(
displayWidth, displayHeight, physicalDisplayWidth, physicalDisplayHeight, density,
pathSpec.trim(), ROTATION_0, 1f /* scale */, physicalPixelDisplaySizeRatio);
+ final int sideOverride = getSideOverride(sideOverrides, ROTATION_0);
+ final Rect[] bounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, false)
+ .getRects();
+ final int rotateDistance = getRotationToOverride(sideOverride, bounds,
+ ROTATION_0 /* defaultRotation */);
+ if (rotateDistance != ROTATION_0) {
+ Collections.rotate(Arrays.asList(bounds), rotateDistance);
+ }
+ final Rect safeInsets = DisplayCutout.computeSafeInsets(displayWidth, displayHeight,
+ waterfallInsets, bounds);
+ final DisplayCutout cutout = new DisplayCutout(safeInsets, waterfallInsets,
+ new Bounds(bounds[BOUNDS_POSITION_LEFT], bounds[BOUNDS_POSITION_TOP],
+ bounds[BOUNDS_POSITION_RIGHT], bounds[BOUNDS_POSITION_BOTTOM], false),
+ cutoutPathParserInfo, sideOverrides);
- final DisplayCutout cutout = new DisplayCutout(
- safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
- cutoutPathParserInfo , false /* copyArguments */);
final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout);
synchronized (CACHE_LOCK) {
sCachedSpec = spec;
@@ -1148,6 +1302,7 @@
sCachedCutout = result;
sCachedWaterfallInsets = waterfallInsets;
sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
+ sCachedSideOverrides = sideOverrides;
}
return result;
}
@@ -1181,7 +1336,10 @@
if (newBounds[i].isEmpty()) continue;
RotationUtils.rotateBounds(newBounds[i], displayBounds, rotation);
}
- Collections.rotate(Arrays.asList(newBounds), -rotation);
+ final int defaultRotation = -rotation;
+ final int override = getSideOverride(mSideOverrides, toRotation);
+ Collections.rotate(Arrays.asList(newBounds),
+ getRotationToOverride(override, newBounds, defaultRotation));
final CutoutPathParserInfo info = getCutoutPathParserInfo();
final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
info.getDisplayWidth(), info.getDisplayHeight(), info.getPhysicalDisplayWidth(),
@@ -1193,51 +1351,87 @@
final DisplayCutout tmp =
DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo);
final Rect safeInsets = DisplayCutout.computeSafeInsets(endWidth, endHeight, tmp);
+ tmp.mSideOverrides = mSideOverrides;
return tmp.replaceSafeInsets(safeInsets);
}
+ private static int getSideOverride(int[] sideOverrides, @Rotation int rotation) {
+ if (sideOverrides == null || sideOverrides.length != 4) {
+ return INVALID_SIDE_OVERRIDE;
+ }
+ return sideOverrides[rotation];
+ }
+
+ /** @return the rotation needed to rotate from the original side to the overridden one. */
+ private static @Rotation int getRotationToOverride(int sideOverride, Rect[] bounds,
+ @Rotation int defaultRotation) {
+ if (sideOverride == INVALID_SIDE_OVERRIDE) {
+ return defaultRotation;
+ }
+ int side = -1;
+ for (int i = 0; i <= BOUNDS_POSITION_BOTTOM; i++) {
+ if (bounds[i].isEmpty()) {
+ continue;
+ }
+ if (side != -1) {
+ // We don't rotate at all when there are multiple non empty cutout bounds.
+ return defaultRotation;
+ }
+ side = i;
+ }
+ if (side == -1) {
+ return defaultRotation;
+ }
+ int rotation = sideOverride - side;
+ if (rotation < 0) {
+ rotation += 4;
+ }
+ return rotation;
+ }
+
/**
* Compute the insets derived from a cutout. This is usually used to populate the safe-insets
* of the cutout via {@link #replaceSafeInsets}.
* @hide
*/
public static Rect computeSafeInsets(int displayW, int displayH, DisplayCutout cutout) {
+ return computeSafeInsets(displayW, displayH, cutout.getWaterfallInsets(),
+ cutout.getBoundingRectsAll());
+ }
+
+ private static Rect computeSafeInsets(int displayW, int displayH, Insets waterFallInsets,
+ Rect[] bounds) {
if (displayW == displayH) {
throw new UnsupportedOperationException("not implemented: display=" + displayW + "x"
- + displayH + " cutout=" + cutout);
+ + displayH + " bounding rects=" + Arrays.toString(bounds));
}
- int leftInset = Math.max(cutout.getWaterfallInsets().left, findCutoutInsetForSide(
- displayW, displayH, cutout.getBoundingRectLeft(), Gravity.LEFT));
- int topInset = Math.max(cutout.getWaterfallInsets().top, findCutoutInsetForSide(
- displayW, displayH, cutout.getBoundingRectTop(), Gravity.TOP));
- int rightInset = Math.max(cutout.getWaterfallInsets().right, findCutoutInsetForSide(
- displayW, displayH, cutout.getBoundingRectRight(), Gravity.RIGHT));
- int bottomInset = Math.max(cutout.getWaterfallInsets().bottom, findCutoutInsetForSide(
- displayW, displayH, cutout.getBoundingRectBottom(), Gravity.BOTTOM));
+ int leftInset = Math.max(waterFallInsets.left, findCutoutInsetForSide(
+ displayW, displayH, bounds[BOUNDS_POSITION_LEFT], Gravity.LEFT));
+ int topInset = Math.max(waterFallInsets.top, findCutoutInsetForSide(
+ displayW, displayH, bounds[BOUNDS_POSITION_TOP], Gravity.TOP));
+ int rightInset = Math.max(waterFallInsets.right, findCutoutInsetForSide(
+ displayW, displayH, bounds[BOUNDS_POSITION_RIGHT], Gravity.RIGHT));
+ int bottomInset = Math.max(waterFallInsets.bottom, findCutoutInsetForSide(
+ displayW, displayH, bounds[BOUNDS_POSITION_BOTTOM], Gravity.BOTTOM));
return new Rect(leftInset, topInset, rightInset, bottomInset);
}
- private static int findCutoutInsetForSide(int displayW, int displayH, Rect boundingRect,
- int gravity) {
+ private static int findCutoutInsetForSide(int displayW, int displayH,
+ @NonNull Rect boundingRect, int gravity) {
if (boundingRect.isEmpty()) {
return 0;
}
int inset = 0;
- switch (gravity) {
- case Gravity.TOP:
- return Math.max(inset, boundingRect.bottom);
- case Gravity.BOTTOM:
- return Math.max(inset, displayH - boundingRect.top);
- case Gravity.LEFT:
- return Math.max(inset, boundingRect.right);
- case Gravity.RIGHT:
- return Math.max(inset, displayW - boundingRect.left);
- default:
- throw new IllegalArgumentException("unknown gravity: " + gravity);
- }
+ return switch (gravity) {
+ case Gravity.TOP -> Math.max(inset, boundingRect.bottom);
+ case Gravity.BOTTOM -> Math.max(inset, displayH - boundingRect.top);
+ case Gravity.LEFT -> Math.max(inset, boundingRect.right);
+ case Gravity.RIGHT -> Math.max(inset, displayW - boundingRect.left);
+ default -> throw new IllegalArgumentException("unknown gravity: " + gravity);
+ };
}
/**
@@ -1293,6 +1487,7 @@
out.writeInt(cutout.mCutoutPathParserInfo.getRotation());
out.writeFloat(cutout.mCutoutPathParserInfo.getScale());
out.writeFloat(cutout.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
+ out.writeIntArray(cutout.mSideOverrides);
}
}
@@ -1348,9 +1543,10 @@
final CutoutPathParserInfo info = new CutoutPathParserInfo(
displayWidth, displayHeight, physicalDisplayWidth, physicalDisplayHeight,
density, cutoutSpec, rotation, scale, physicalPixelDisplaySizeRatio);
+ final int[] sideOverrides = in.createIntArray();
- return new DisplayCutout(
- safeInsets, waterfallInsets, bounds, info, false /* copyArguments */);
+ return new DisplayCutout(safeInsets, waterfallInsets,
+ new Bounds(bounds, false /* copyArguments */), info, sideOverrides);
}
public DisplayCutout get() {
@@ -1382,8 +1578,10 @@
mInner.mCutoutPathParserInfo.getRotation(),
scale,
mInner.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
+ final int[] sideOverrides = mInner.mSideOverrides;
- mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info);
+ mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info,
+ sideOverrides);
}
@Override
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 31d759e..18080e4 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -287,6 +287,16 @@
}
/**
+ * Called when a display hdcp levels change event is received.
+ *
+ * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
+ * @param connectedLevel the new connected HDCP level
+ * @param maxLevel the maximum HDCP level
+ */
+ public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+ }
+
+ /**
* Represents a mapping between a UID and an override frame rate
*/
public static class FrameRateOverride {
@@ -374,4 +384,11 @@
onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
}
+ // Called from native code.
+ @SuppressWarnings("unused")
+ private void dispatchHdcpLevelsChanged(long physicalDisplayId, int connectedLevel,
+ int maxLevel) {
+ onHdcpLevelsChanged(physicalDisplayId, connectedLevel, maxLevel);
+ }
+
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index f7b9aa2..079991a 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -76,7 +76,7 @@
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(runner.getTypes()),
runner.getTypes());
- releaseControls(mControl.getControls());
+ InsetsController.releaseControls(mControl.getControls());
mMainThreadHandler.post(() ->
mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown));
}
@@ -130,12 +130,6 @@
});
}
- private void releaseControls(SparseArray<InsetsSourceControl> controls) {
- for (int i = controls.size() - 1; i >= 0; i--) {
- controls.valueAt(i).release(SurfaceControl::release);
- }
- }
-
@Override
@UiThread
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 147c15b..dd09157 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1353,6 +1353,9 @@
});
}
+ // The leashes are copied, but they won't be used.
+ releaseControls(controls);
+
// The requested visibilities should be delayed as well. Otherwise, we might override
// the insets visibility before playing animation.
setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
@@ -1422,6 +1425,12 @@
}
}
+ static void releaseControls(SparseArray<InsetsSourceControl> controls) {
+ for (int i = controls.size() - 1; i >= 0; i--) {
+ controls.valueAt(i).release(SurfaceControl::release);
+ }
+ }
+
// TODO(b/242962223): Make this setter restrictive.
@Override
public void setSystemDrivenInsetsAnimationLoggingListener(
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index cabab6c..0927d45 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -17,7 +17,6 @@
package android.view;
import static android.view.InsetsSourceProto.FRAME;
-import static android.view.InsetsSourceProto.TYPE;
import static android.view.InsetsSourceProto.TYPE_NUMBER;
import static android.view.InsetsSourceProto.VISIBLE;
import static android.view.InsetsSourceProto.VISIBLE_FRAME;
@@ -353,13 +352,12 @@
*/
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(TYPE, WindowInsets.Type.toString(mType));
- proto.write(TYPE_NUMBER, mType);
mFrame.dumpDebug(proto, FRAME);
if (mVisibleFrame != null) {
mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
}
proto.write(VISIBLE, mVisible);
+ proto.write(TYPE_NUMBER, mType);
proto.end(token);
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 34b2884..0ce61bb 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -21,11 +21,11 @@
import static android.view.InsetsController.DEBUG;
import static android.view.InsetsSourceConsumerProto.ANIMATION_STATE;
import static android.view.InsetsSourceConsumerProto.HAS_WINDOW_FOCUS;
-import static android.view.InsetsSourceConsumerProto.INTERNAL_INSETS_TYPE;
import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
+import static android.view.InsetsSourceConsumerProto.TYPE_NUMBER;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -393,7 +393,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(INTERNAL_INSETS_TYPE, WindowInsets.Type.toString(mType));
proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
proto.write(IS_REQUESTED_VISIBLE, isShowRequested());
if (mSourceControl != null) {
@@ -406,6 +405,7 @@
mPendingVisibleFrame.dumpDebug(proto, PENDING_VISIBLE_FRAME);
}
proto.write(ANIMATION_STATE, mAnimationState);
+ proto.write(TYPE_NUMBER, mType);
proto.end(token);
}
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 7ea93f5..527c7ed 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -20,7 +20,7 @@
import static android.graphics.PointProto.Y;
import static android.view.InsetsSourceControlProto.LEASH;
import static android.view.InsetsSourceControlProto.POSITION;
-import static android.view.InsetsSourceControlProto.TYPE;
+import static android.view.InsetsSourceControlProto.TYPE_NUMBER;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -244,8 +244,6 @@
*/
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(TYPE, WindowInsets.Type.toString(mType));
-
final long surfaceToken = proto.start(POSITION);
proto.write(X, mSurfacePosition.x);
proto.write(Y, mSurfacePosition.y);
@@ -254,6 +252,8 @@
if (mLeash != null) {
mLeash.dumpDebug(proto, LEASH);
}
+
+ proto.write(TYPE_NUMBER, mType);
proto.end(token);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cbbe785..b957b31 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -22,7 +22,6 @@
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
@@ -38,7 +37,6 @@
import android.annotation.Size;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
@@ -53,13 +51,8 @@
import android.hardware.OverlayProperties;
import android.hardware.SyncFence;
import android.hardware.display.DeviceProductInfo;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
-import android.hardware.display.IDisplayManager;
-import android.hardware.display.IVirtualDisplayCallback;
-import android.hardware.display.VirtualDisplay;
import android.hardware.graphics.common.DisplayDecorationSupport;
import android.opengl.EGLDisplay;
import android.opengl.EGLSync;
@@ -68,8 +61,6 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -2355,92 +2346,6 @@
}
/**
- * Because this API is now going through {@link DisplayManager}, orientation and displayRect
- * will automatically be computed based on configuration changes. Because of this, the params
- * orientation and displayRect are ignored
- *
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code VirtualDisplay#resize(int, int, int)} instead.",
- trackingBug = 247078497)
- public static void setDisplayProjection(IBinder displayToken, int orientation,
- Rect layerStackRect, Rect displayRect) {
- DisplayManagerGlobal.getInstance().resizeVirtualDisplay(
- IVirtualDisplayCallback.Stub.asInterface(displayToken), layerStackRect.width(),
- layerStackRect.height(), 1);
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} with flag "
- + " {@code VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for mirroring instead.",
- trackingBug = 247078497)
- public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
- IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
- if (b == null) {
- throw new UnsupportedOperationException();
- }
-
- IDisplayManager dm = IDisplayManager.Stub.asInterface(b);
- try {
- dm.setDisplayIdToMirror(displayToken, layerStack);
- } catch (RemoteException e) {
- throw new UnsupportedOperationException(e);
- }
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code VirtualDisplay#setSurface(Surface)} instead.",
- trackingBug = 247078497)
- public static void setDisplaySurface(IBinder displayToken, Surface surface) {
- IVirtualDisplayCallback virtualDisplayCallback =
- IVirtualDisplayCallback.Stub.asInterface(displayToken);
- DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
- dm.setVirtualDisplaySurface(virtualDisplayCallback, surface);
- }
-
- /**
- * Secure is no longer supported because this is only called from outside system which cannot
- * create secure displays.
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} or "
- + "{@code DisplayManager#createVirtualDisplay()} instead.",
- trackingBug = 247078497)
- public static IBinder createDisplay(String name, boolean secure) {
- if (name == null) {
- throw new IllegalArgumentException("name must not be null");
- }
-
- // We don't have a size yet so pass in 1 for width and height since 0 is invalid
- VirtualDisplay vd = DisplayManager.createVirtualDisplay(name, 1 /* width */, 1 /* height */,
- INVALID_DISPLAY, null /* Surface */);
- return vd == null ? null : vd.getToken().asBinder();
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
- publicAlternatives = "Use {@code VirtualDisplay#release()} instead.",
- trackingBug = 247078497)
- public static void destroyDisplay(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
-
- DisplayManagerGlobal.getInstance().releaseVirtualDisplay(
- IVirtualDisplayCallback.Stub.asInterface(displayToken));
- }
-
- /**
* Returns whether protected content is supported in GPU composition.
* @hide
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cbafd1c..442ea66 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -32,6 +32,7 @@
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
@@ -2309,6 +2310,7 @@
protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
+ private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
static {
EMPTY_STATE_SET = StateSet.get(0);
@@ -2393,6 +2395,7 @@
| StateSet.VIEW_STATE_PRESSED);
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
+ sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
}
/**
@@ -10757,11 +10760,11 @@
return;
}
- session.internalNotifyViewTreeEvent(/* started= */ true);
+ session.notifyViewTreeEvent(/* started= */ true);
try {
dispatchProvideContentCaptureStructure();
} finally {
- session.internalNotifyViewTreeEvent(/* started= */ false);
+ session.notifyViewTreeEvent(/* started= */ false);
}
}
@@ -33056,7 +33059,10 @@
}
private float getSizePercentage() {
- if (mResources == null || getVisibility() != VISIBLE) {
+ float alpha = mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
+ int visibility = mViewFlags & VISIBILITY_MASK;
+
+ if (mResources == null || alpha == 0 || visibility != VISIBLE) {
return 0;
}
@@ -33084,22 +33090,26 @@
ViewRootImpl viewRootImpl = getViewRootImpl();
float sizePercentage = getSizePercentage();
int frameRateCateogry = calculateFrameRateCategory(sizePercentage);
- if (sToolkitSetFrameRateReadOnlyFlagValue && viewRootImpl != null
- && sizePercentage > 0) {
- if (mPreferredFrameRate < 0) {
- if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
- frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) {
- frameRateCateogry = FRAME_RATE_CATEGORY_LOW;
- } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) {
- frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL;
- } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) {
- frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
+ if (viewRootImpl != null && sizePercentage > 0) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ if (mPreferredFrameRate < 0) {
+ if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_LOW;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
+ }
+ } else {
+ viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
}
- } else {
- viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
+ viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
}
- viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
+ if (sToolkitMetricsForFrameRateDecisionFlagValue) {
+ viewRootImpl.recordViewPercentage(sizePercentage);
+ }
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e83488e..32afe06 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -111,6 +111,7 @@
import android.app.ResourcesManager;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
+import android.app.servertransaction.WindowStateResizeItem;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -208,7 +209,6 @@
import android.view.autofill.AutofillManager;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
-import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -235,6 +235,7 @@
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.SurfaceCallbackHelper;
+import com.android.modules.expresslog.Counter;
import com.android.window.flags.Flags;
import java.io.IOException;
@@ -252,7 +253,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
-
/**
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
@@ -528,8 +528,6 @@
// Set to true to stop input during an Activity Transition.
boolean mPausedForTransition = false;
- boolean mLastInCompatMode = false;
-
SurfaceHolder.Callback2 mSurfaceHolderCallback;
BaseSurfaceHolder mSurfaceHolder;
boolean mIsCreating;
@@ -830,6 +828,8 @@
private boolean mInsetsAnimationRunning;
private long mPreviousFrameDrawnTime = -1;
+ // The largest view size percentage to the display size. Used on trace to collect metric.
+ private float mLargestChildPercentage = 0.0f;
/**
* The resolved pointer icon type requested by this window.
@@ -1069,6 +1069,7 @@
private String mTag = TAG;
private String mFpsTraceName;
+ private String mLargestViewTraceName;
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1320,6 +1321,7 @@
attrs = mWindowAttributes;
setTag();
mFpsTraceName = "FPS of " + getTitle();
+ mLargestViewTraceName = "Largest view percentage(per hundred) of " + getTitle();
if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
& WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0
@@ -1375,11 +1377,6 @@
}
if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs);
- if (!compatibilityInfo.supportsScreen()) {
- attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- mLastInCompatMode = true;
- }
-
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
@@ -1913,10 +1910,6 @@
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
- // Preserve compatible window flag if exists.
- final int compatibleWindowFlag = mWindowAttributes.privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-
// Preserve system UI visibility.
final int systemUiVisibility = mWindowAttributes.systemUiVisibility;
final int subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
@@ -1948,8 +1941,7 @@
mWindowAttributes.subtreeSystemUiVisibility = subtreeSystemUiVisibility;
mWindowAttributes.insetsFlags.appearance = appearance;
mWindowAttributes.insetsFlags.behavior = behavior;
- mWindowAttributes.privateFlags |= compatibleWindowFlag
- | appearanceAndBehaviorPrivateFlags;
+ mWindowAttributes.privateFlags |= appearanceAndBehaviorPrivateFlags;
if (mWindowAttributes.preservePreviousSurfaceInsets) {
// Restore old surface insets.
@@ -2020,26 +2012,24 @@
}
/** Handles messages {@link #MSG_RESIZED} and {@link #MSG_RESIZED_REPORT}. */
- private void handleResized(int msg, SomeArgs args) {
+ private void handleResized(ClientWindowFrames frames, boolean reportDraw,
+ MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout,
+ boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
if (!mAdded) {
return;
}
- final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
- final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
- final boolean forceNextWindowRelayout = args.argi1 != 0;
- final int displayId = args.argi3;
- final boolean dragResizing = args.argi5 != 0;
-
final Rect frame = frames.frame;
final Rect displayFrame = frames.displayFrame;
final Rect attachedFrame = frames.attachedFrame;
if (mTranslator != null) {
+ mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
mTranslator.translateRectInScreenToAppWindow(frame);
mTranslator.translateRectInScreenToAppWindow(displayFrame);
mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
+ mInsetsController.onStateChanged(insetsState);
final float compatScale = frames.compatScale;
final boolean frameChanged = !mWinFrame.equals(frame);
final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
@@ -2048,8 +2038,8 @@
final boolean displayChanged = mDisplay.getDisplayId() != displayId;
final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale;
final boolean dragResizingChanged = mPendingDragResizing != dragResizing;
- if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged
- && !displayChanged && !forceNextWindowRelayout
+ if (!reportDraw && !frameChanged && !configChanged && !attachedFrameChanged
+ && !displayChanged && !forceLayout
&& !compatScaleChanged && !dragResizingChanged) {
return;
}
@@ -2081,11 +2071,11 @@
}
}
- mForceNextWindowRelayout |= forceNextWindowRelayout;
- mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
- mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
+ mForceNextWindowRelayout |= forceLayout;
+ mPendingAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ mSyncSeqId = syncSeqId > mSyncSeqId ? syncSeqId : mSyncSeqId;
- if (msg == MSG_RESIZED_REPORT) {
+ if (reportDraw) {
reportNextDraw("resized");
}
@@ -3150,21 +3140,6 @@
final boolean shouldOptimizeMeasure = shouldOptimizeMeasure(lp);
WindowManager.LayoutParams params = null;
- CompatibilityInfo compatibilityInfo =
- mDisplay.getDisplayAdjustments().getCompatibilityInfo();
- if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
- params = lp;
- mFullRedrawNeeded = true;
- mLayoutRequested = true;
- if (mLastInCompatMode) {
- params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- mLastInCompatMode = false;
- } else {
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- mLastInCompatMode = true;
- }
- }
-
Rect frame = mWinFrame;
if (mFirst) {
mFullRedrawNeeded = true;
@@ -4127,7 +4102,7 @@
final ContentCaptureManager manager = mAttachInfo.mContentCaptureManager;
if (manager != null && mAttachInfo.mContentCaptureEvents != null) {
- final MainContentCaptureSession session = manager.getMainContentCaptureSession();
+ final ContentCaptureSession session = manager.getMainContentCaptureSession();
session.notifyContentCaptureEvents(mAttachInfo.mContentCaptureEvents);
}
mAttachInfo.mContentCaptureEvents = null;
@@ -4766,6 +4741,10 @@
long fps = NANOS_PER_SEC / timeDiff;
Trace.setCounter(mFpsTraceName, fps);
mPreviousFrameDrawnTime = expectedDrawnTime;
+
+ long percentage = (long) (mLargestChildPercentage * 100);
+ Trace.setCounter(mLargestViewTraceName, percentage);
+ mLargestChildPercentage = 0.0f;
}
private void reportDrawFinished(@Nullable Transaction t, int seqId) {
@@ -5040,7 +5019,7 @@
// Initial dispatch of window bounds to content capture
if (mAttachInfo.mContentCaptureManager != null) {
- MainContentCaptureSession session =
+ ContentCaptureSession session =
mAttachInfo.mContentCaptureManager.getMainContentCaptureSession();
session.notifyWindowBoundsChanged(session.getId(),
getConfiguration().windowConfiguration.getBounds());
@@ -5086,6 +5065,7 @@
if (DEBUG_FPS) {
trackFPS();
}
+
if (sToolkitMetricsForFrameRateDecisionFlagValue) {
collectFrameRateDecisionMetrics();
}
@@ -6250,8 +6230,17 @@
case MSG_RESIZED:
case MSG_RESIZED_REPORT: {
final SomeArgs args = (SomeArgs) msg.obj;
- mInsetsController.onStateChanged((InsetsState) args.arg3);
- handleResized(msg.what, args);
+ final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
+ final boolean reportDraw = msg.what == MSG_RESIZED_REPORT;
+ final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
+ final InsetsState insetsState = (InsetsState) args.arg3;
+ final boolean forceLayout = args.argi1 != 0;
+ final boolean alwaysConsumeSystemBars = args.argi2 != 0;
+ final int displayId = args.argi3;
+ final int syncSeqId = args.argi4;
+ final boolean dragResizing = args.argi5 != 0;
+ handleResized(frames, reportDraw, mergedConfiguration, insetsState, forceLayout,
+ alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
args.recycle();
break;
}
@@ -8815,7 +8804,7 @@
mSurfaceControl.setTransformHint(transformHint);
if (mAttachInfo.mContentCaptureManager != null) {
- MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+ ContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
.getMainContentCaptureSession();
mainSession.notifyWindowBoundsChanged(mainSession.getId(),
getConfiguration().windowConfiguration.getBounds());
@@ -9397,20 +9386,8 @@
boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
SomeArgs args = SomeArgs.obtain();
- final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
- if (sameProcessCall) {
- insetsState = new InsetsState(insetsState, true /* copySource */);
- }
- if (mTranslator != null) {
- mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
- }
- if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
- ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchResized",
- getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
- }
- args.arg1 = sameProcessCall ? new ClientWindowFrames(frames) : frames;
- args.arg2 = sameProcessCall && mergedConfiguration != null
- ? new MergedConfiguration(mergedConfiguration) : mergedConfiguration;
+ args.arg1 = frames;
+ args.arg2 = mergedConfiguration;
args.arg3 = insetsState;
args.argi1 = forceLayout ? 1 : 0;
args.argi2 = alwaysConsumeSystemBars ? 1 : 0;
@@ -9723,6 +9700,9 @@
} else {
q.mReceiver.finishInputEvent(q.mEvent, handled);
}
+ if (q.mEvent instanceof KeyEvent) {
+ logHandledSystemKey((KeyEvent) q.mEvent, handled);
+ }
} else {
q.mEvent.recycleIfNeededAfterDispatch();
}
@@ -9730,6 +9710,19 @@
recycleQueuedInputEvent(q);
}
+ private void logHandledSystemKey(KeyEvent event, boolean handled) {
+ final int keyCode = event.getKeyCode();
+ if (keyCode != KeyEvent.KEYCODE_STEM_PRIMARY) {
+ return;
+ }
+ if (event.isDown() && event.getRepeatCount() == 0 && handled) {
+ // Initial DOWN event is handled. Log the stem primary key press.
+ Counter.logIncrementWithUid(
+ "input.value_app_handled_stem_primary_key_gestures_count",
+ Process.myUid());
+ }
+ }
+
static boolean isTerminalInputEvent(InputEvent event) {
if (event instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent)event;
@@ -10817,9 +10810,10 @@
}
}
- static class W extends IWindow.Stub {
+ static class W extends IWindow.Stub implements WindowStateResizeItem.ResizeListener {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
+ private boolean mIsFromResizeItem;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
@@ -10827,17 +10821,46 @@
}
@Override
+ public void onExecutingWindowStateResizeItem() {
+ mIsFromResizeItem = true;
+ }
+
+ @Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
boolean dragResizing) {
+ final boolean isFromResizeItem = mIsFromResizeItem;
+ mIsFromResizeItem = false;
// Although this is a AIDL method, it will only be triggered in local process through
// either WindowStateResizeItem or WindowlessWindowManager.
final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
- forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
+ if (viewAncestor == null) {
+ return;
}
+ if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
+ ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#resized",
+ viewAncestor.getInsetsController().getHost().getInputMethodManager(),
+ null /* icProto */);
+ }
+ // If the UI thread is the same as the current thread that is dispatching
+ // WindowStateResizeItem, then it can run directly.
+ if (isFromResizeItem && viewAncestor.mHandler.getLooper()
+ == ActivityThread.currentActivityThread().getLooper()) {
+ viewAncestor.handleResized(frames, reportDraw, mergedConfiguration, insetsState,
+ forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
+ return;
+ }
+ // The the parameters from WindowStateResizeItem are already copied.
+ final boolean needCopy =
+ !isFromResizeItem && (Binder.getCallingPid() == Process.myPid());
+ if (needCopy) {
+ insetsState = new InsetsState(insetsState, true /* copySource */);
+ frames = new ClientWindowFrames(frames);
+ mergedConfiguration = new MergedConfiguration(mergedConfiguration);
+ }
+ viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
+ forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
}
@Override
@@ -11912,6 +11935,12 @@
if (syncBuffer) {
boolean result = mBlastBufferQueue.syncNextTransaction(transaction -> {
+ Runnable timeoutRunnable = () -> Log.e(mTag,
+ "Failed to submit the sync transaction after 4s. Likely to ANR "
+ + "soon");
+ mHandler.postDelayed(timeoutRunnable, 4L * Build.HW_TIMEOUT_MULTIPLIER);
+ transaction.addTransactionCommittedListener(mSimpleExecutor,
+ () -> mHandler.removeCallbacks(timeoutRunnable));
surfaceSyncGroup.addTransaction(transaction);
surfaceSyncGroup.markSyncReady();
});
@@ -12175,7 +12204,8 @@
|| motionEventAction == MotionEvent.ACTION_UP;
boolean undesiredType = windowType == TYPE_INPUT_METHOD;
// use toolkitSetFrameRate flag to gate the change
- return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue;
+ return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue
+ && getFrameRateBoostOnTouchEnabled();
}
/**
@@ -12250,6 +12280,15 @@
return mIsFrameRateBoosting;
}
+ /**
+ * Get the value of mFrameRateBoostOnTouchEnabled
+ * Can be used to checked if touch boost is enabled. The default value is true.
+ */
+ @VisibleForTesting
+ public boolean getFrameRateBoostOnTouchEnabled() {
+ return mWindowAttributes.getFrameRateBoostOnTouchEnabled();
+ }
+
private void boostFrameRate(int boostTimeOut) {
mIsFrameRateBoosting = true;
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
@@ -12279,4 +12318,10 @@
void setBackKeyCallbackForWindowlessWindow(@NonNull Predicate<KeyEvent> callback) {
mWindowlessBackKeyCallback = callback;
}
+
+ void recordViewPercentage(float percentage) {
+ if (!Trace.isEnabled()) return;
+ // Record the largest view of percentage to the display size.
+ mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage);
+ }
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 87537fbc..7bae7ec 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -334,6 +334,9 @@
private boolean mOverlayWithDecorCaptionEnabled = true;
private boolean mCloseOnSwipeEnabled = false;
+ private static boolean sToolkitSetFrameRateReadOnlyFlagValue =
+ android.view.flags.Flags.toolkitSetFrameRateReadOnly();
+
// The current window attributes.
@UnsupportedAppUsage
private final WindowManager.LayoutParams mWindowAttributes =
@@ -1373,6 +1376,39 @@
}
/**
+ * Sets whether the frame rate touch boost is enabled for this Window.
+ * When enabled, the frame rate will be boosted when a user touches the Window.
+ *
+ * @param enabled whether the frame rate touch boost is enabled.
+ * @see #getFrameRateBoostOnTouchEnabled()
+ * @see WindowManager.LayoutParams#setFrameRateBoostOnTouchEnabled(boolean)
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setFrameRateBoostOnTouchEnabled(boolean enabled) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ final WindowManager.LayoutParams attrs = getAttributes();
+ attrs.setFrameRateBoostOnTouchEnabled(enabled);
+ dispatchWindowAttributesChanged(attrs);
+ }
+ }
+
+ /**
+ * Get whether frame rate touch boost is enabled
+ * {@link #setFrameRateBoostOnTouchEnabled(boolean)}
+ *
+ * @return whether the frame rate touch boost is enabled.
+ * @see #setFrameRateBoostOnTouchEnabled(boolean)
+ * @see WindowManager.LayoutParams#getFrameRateBoostOnTouchEnabled()
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public boolean getFrameRateBoostOnTouchEnabled() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return getAttributes().getFrameRateBoostOnTouchEnabled();
+ }
+ return true;
+ }
+
+ /**
* If {@code isPreferred} is true, this method requests that the connected display does minimal
* post processing when this window is visible on the screen. Otherwise, it requests that the
* display switches back to standard image processing.
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 57a4161..921afaa 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -38,6 +38,8 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.graphics.Insets;
@@ -1519,6 +1521,9 @@
}
/** @hide */
+ @TestApi
+ @NonNull
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
public static String toString(@InsetsType int types) {
StringBuilder result = new StringBuilder();
if ((types & STATUS_BARS) != 0) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1712fd3..f76822f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3143,13 +3143,6 @@
@UnsupportedAppUsage
public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 1 << 6;
- /** Window flag: special flag to limit the size of the window to be
- * original size ([320x480] x density). Used to create window for applications
- * running under compatibility mode.
- *
- * {@hide} */
- public static final int PRIVATE_FLAG_COMPATIBLE_WINDOW = 1 << 7;
-
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
* it is created.
@@ -3345,7 +3338,6 @@
SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION,
PRIVATE_FLAG_NO_MOVE_ANIMATION,
- PRIVATE_FLAG_COMPATIBLE_WINDOW,
PRIVATE_FLAG_SYSTEM_ERROR,
PRIVATE_FLAG_OPTIMIZE_MEASURE,
PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
@@ -3399,10 +3391,6 @@
equals = PRIVATE_FLAG_NO_MOVE_ANIMATION,
name = "NO_MOVE_ANIMATION"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_COMPATIBLE_WINDOW,
- equals = PRIVATE_FLAG_COMPATIBLE_WINDOW,
- name = "COMPATIBLE_WINDOW"),
- @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_SYSTEM_ERROR,
equals = PRIVATE_FLAG_SYSTEM_ERROR,
name = "SYSTEM_ERROR"),
@@ -4344,6 +4332,13 @@
private float mDesiredHdrHeadroom = 0;
/**
+ * For variable refresh rate project.
+ */
+ private boolean mFrameRateBoostOnTouch = true;
+ private static boolean sToolkitSetFrameRateReadOnlyFlagValue =
+ android.view.flags.Flags.toolkitSetFrameRateReadOnly();
+
+ /**
* Carries the requests about {@link WindowInsetsController.Appearance} and
* {@link WindowInsetsController.Behavior} to the system windows which can produce insets.
*
@@ -4778,6 +4773,32 @@
}
/**
+ * Set the value whether we should enable Touch Boost
+ *
+ * @param enabled Whether we should enable Touch Boost
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setFrameRateBoostOnTouchEnabled(boolean enabled) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mFrameRateBoostOnTouch = enabled;
+ }
+ }
+
+ /**
+ * Get the value whether we should enable touch boost as set
+ * by {@link #setFrameRateBoostOnTouchEnabled(boolean)}
+ *
+ * @return A boolean value to indicate whether we should enable touch boost
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public boolean getFrameRateBoostOnTouchEnabled() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return mFrameRateBoostOnTouch;
+ }
+ return true;
+ }
+
+ /**
* <p>
* Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount},
* but instead of dimmed, the content behind the window will be blurred (or combined with
@@ -4928,6 +4949,9 @@
out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
out.writeInt(mDisplayFlags);
out.writeFloat(mDesiredHdrHeadroom);
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ out.writeBoolean(mFrameRateBoostOnTouch);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -5000,6 +5024,9 @@
paramsForRotation = in.createTypedArray(LayoutParams.CREATOR);
mDisplayFlags = in.readInt();
mDesiredHdrHeadroom = in.readFloat();
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mFrameRateBoostOnTouch = in.readBoolean();
+ }
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -5336,6 +5363,12 @@
changes |= LAYOUT_CHANGED;
}
+ if (sToolkitSetFrameRateReadOnlyFlagValue
+ && mFrameRateBoostOnTouch != o.mFrameRateBoostOnTouch) {
+ mFrameRateBoostOnTouch = o.mFrameRateBoostOnTouch;
+ changes |= LAYOUT_CHANGED;
+ }
+
return changes;
}
@@ -5558,6 +5591,11 @@
sb.append(prefix).append(" forciblyShownTypes=").append(
WindowInsets.Type.toString(forciblyShownTypes));
}
+ if (sToolkitSetFrameRateReadOnlyFlagValue && mFrameRateBoostOnTouch) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" frameRateBoostOnTouch=");
+ sb.append(mFrameRateBoostOnTouch);
+ }
if (paramsForRotation != null && paramsForRotation.length != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" paramsForRotation:");
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 53aed49..49a2843 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -5400,13 +5400,10 @@
public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT =
new AccessibilityAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content forward.
*
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
* this element should also add the relevant directional scroll actions of
@@ -5447,12 +5444,10 @@
public static final AccessibilityAction ACTION_SCROLL_FORWARD =
new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content backward.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation,
* this element should also add the relevant directional scroll actions of
@@ -5647,48 +5642,40 @@
@NonNull public static final AccessibilityAction ACTION_SCROLL_IN_DIRECTION =
new AccessibilityAction(R.id.accessibilityActionScrollInDirection);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content up.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
public static final AccessibilityAction ACTION_SCROLL_UP =
new AccessibilityAction(R.id.accessibilityActionScrollUp);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content left.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
public static final AccessibilityAction ACTION_SCROLL_LEFT =
new AccessibilityAction(R.id.accessibilityActionScrollLeft);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content down.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
public static final AccessibilityAction ACTION_SCROLL_DOWN =
new AccessibilityAction(R.id.accessibilityActionScrollDown);
+ // TODO(316638728): restore ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT in javadoc
/**
* Action to scroll the node content right.
- * <p>
- * <strong>Arguments:</strong>
- * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument.
- * </p>
+ *
* <p>The UI element that implements this should send a
* {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
*/
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index b3359b7..70d8abe 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -23,9 +23,6 @@
import android.annotation.FlaggedApi;
import android.annotation.InterpolatorRes;
import android.annotation.TestApi;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
-import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
@@ -55,18 +52,6 @@
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
- /**
- * For apps targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} and above,
- * this change ID enables to use expectedPresentationTime instead of the frameTime
- * for the frame start time .
- *
- * @hide
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM)
- @Overridable
- public static final long OVERRIDE_ENABLE_EXPECTED_PRSENTATION_TIME = 278730197L;
-
private static boolean sExpectedPresentationTimeFlagValue;
static {
sExpectedPresentationTimeFlagValue = expectedPresentationTimeReadOnly();
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 44b4353..70c899f 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -17,10 +17,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import java.util.ArrayList;
+
/**
* A session that is explicitly created by the app (and hence is a descendant of
* {@link MainContentCaptureSession}).
@@ -40,17 +46,30 @@
}
@Override
- MainContentCaptureSession getMainCaptureSession() {
- if (mParent instanceof MainContentCaptureSession) {
- return (MainContentCaptureSession) mParent;
- }
+ ContentCaptureSession getMainCaptureSession() {
return mParent.getMainCaptureSession();
}
@Override
+ void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
+ getMainCaptureSession().start(token, shareableActivityToken, component, flags);
+ }
+
+ @Override
+ boolean isDisabled() {
+ return getMainCaptureSession().isDisabled();
+ }
+
+ @Override
+ boolean setDisabled(boolean disabled) {
+ return getMainCaptureSession().setDisabled(disabled);
+ }
+
+ @Override
ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
- getMainCaptureSession().notifyChildSessionStarted(mId, child.mId, clientContext);
+ internalNotifyChildSessionStarted(mId, child.mId, clientContext);
return child;
}
@@ -61,51 +80,80 @@
@Override
public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
- getMainCaptureSession().notifyContextUpdated(mId, context);
+ internalNotifyContextUpdated(mId, context);
}
@Override
void onDestroy() {
- getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
+ internalNotifyChildSessionFinished(mParent.mId, mId);
}
@Override
- void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
- getMainCaptureSession().notifyViewAppeared(mId, node);
+ void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext) {
+ getMainCaptureSession()
+ .internalNotifyChildSessionStarted(parentSessionId, childSessionId, clientContext);
}
@Override
- void internalNotifyViewDisappeared(@NonNull AutofillId id) {
- getMainCaptureSession().notifyViewDisappeared(mId, id);
+ void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+ getMainCaptureSession().internalNotifyChildSessionFinished(parentSessionId, childSessionId);
}
@Override
- void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
- getMainCaptureSession().notifyViewTextChanged(mId, id, text);
+ void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+ getMainCaptureSession().internalNotifyContextUpdated(sessionId, context);
}
@Override
- void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
- getMainCaptureSession().notifyViewInsetsChanged(mId, viewInsets);
+ void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+ getMainCaptureSession().internalNotifyViewAppeared(sessionId, node);
}
@Override
- public void internalNotifyViewTreeEvent(boolean started) {
- getMainCaptureSession().notifyViewTreeEvent(mId, started);
+ void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+ getMainCaptureSession().internalNotifyViewDisappeared(sessionId, id);
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(
+ int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+ getMainCaptureSession().internalNotifyViewTextChanged(sessionId, id, text);
+ }
+
+ @Override
+ void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+ getMainCaptureSession().internalNotifyViewInsetsChanged(mId, viewInsets);
+ }
+
+ @Override
+ public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
+ getMainCaptureSession().internalNotifyViewTreeEvent(sessionId, started);
}
@Override
void internalNotifySessionResumed() {
- getMainCaptureSession().notifySessionResumed();
+ getMainCaptureSession().internalNotifySessionResumed();
}
@Override
void internalNotifySessionPaused() {
- getMainCaptureSession().notifySessionPaused();
+ getMainCaptureSession().internalNotifySessionPaused();
}
@Override
boolean isContentCaptureEnabled() {
return getMainCaptureSession().isContentCaptureEnabled();
}
+
+ @Override
+ public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+ getMainCaptureSession().notifyWindowBoundsChanged(sessionId, bounds);
+ }
+
+ @Override
+ public void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+ getMainCaptureSession().notifyContentCaptureEvents(contentCaptureEvents);
+ }
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index a829747..bcef37f 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -499,10 +499,14 @@
@Nullable
@GuardedBy("mLock")
- private Handler mHandler;
+ private Handler mUiHandler;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private Handler mContentCaptureHandler;
@GuardedBy("mLock")
- private MainContentCaptureSession mMainSession;
+ private ContentCaptureSession mMainSession;
@Nullable // set on-demand by addDumpable()
private Dumper mDumpable;
@@ -587,11 +591,10 @@
*/
@NonNull
@UiThread
- public MainContentCaptureSession getMainContentCaptureSession() {
+ public ContentCaptureSession getMainContentCaptureSession() {
synchronized (mLock) {
if (mMainSession == null) {
- mMainSession = new MainContentCaptureSession(
- mContext, this, prepareContentCaptureHandler(), mService);
+ mMainSession = prepareMainSession();
if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
}
return mMainSession;
@@ -600,15 +603,36 @@
@NonNull
@GuardedBy("mLock")
- private Handler prepareContentCaptureHandler() {
- if (mHandler == null) {
- if (runOnBackgroundThreadEnabled()) {
- mHandler = BackgroundThread.getHandler();
- } else {
- mHandler = Handler.createAsync(Looper.getMainLooper());
- }
+ private ContentCaptureSession prepareMainSession() {
+ if (runOnBackgroundThreadEnabled()) {
+ return new MainContentCaptureSessionV2(
+ mContext,
+ this,
+ prepareUiHandler(),
+ prepareContentCaptureHandler(),
+ mService
+ );
+ } else {
+ return new MainContentCaptureSession(mContext, this, prepareUiHandler(), mService);
}
- return mHandler;
+ }
+
+ @NonNull
+ @GuardedBy("mLock")
+ private Handler prepareContentCaptureHandler() {
+ if (mContentCaptureHandler == null) {
+ mContentCaptureHandler = BackgroundThread.getHandler();
+ }
+ return mContentCaptureHandler;
+ }
+
+ @NonNull
+ @GuardedBy("mLock")
+ private Handler prepareUiHandler() {
+ if (mUiHandler == null) {
+ mUiHandler = Handler.createAsync(Looper.getMainLooper());
+ }
+ return mUiHandler;
}
/** @hide */
@@ -726,7 +750,7 @@
public boolean isContentCaptureEnabled() {
if (mOptions.lite) return false;
- final MainContentCaptureSession mainSession;
+ final ContentCaptureSession mainSession;
synchronized (mLock) {
mainSession = mMainSession;
}
@@ -777,7 +801,7 @@
Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
}
- MainContentCaptureSession mainSession;
+ ContentCaptureSession mainSession;
synchronized (mLock) {
if (enabled) {
mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_APP;
@@ -803,7 +827,7 @@
final boolean flagSecureEnabled =
(params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
- MainContentCaptureSession mainSession;
+ ContentCaptureSession mainSession;
boolean alreadyDisabledByApp;
synchronized (mLock) {
alreadyDisabledByApp = (mFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0;
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index bb815c0..0ca36ba2 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -27,9 +27,13 @@
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.ComponentName;
import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
import android.util.DebugUtils;
import android.util.Log;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
@@ -37,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
@@ -60,6 +65,18 @@
private static final SecureRandom ID_GENERATOR = new SecureRandom();
/**
+ * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+ * @hide
+ */
+ public static final String EXTRA_BINDER = "binder";
+
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
+ * @hide
+ */
+ public static final String EXTRA_ENABLED_STATE = "enabled";
+
+ /**
* Initial state, when there is no session.
*
* @hide
@@ -262,7 +279,19 @@
/** @hide */
@NonNull
- abstract MainContentCaptureSession getMainCaptureSession();
+ abstract ContentCaptureSession getMainCaptureSession();
+
+ abstract void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags);
+
+ abstract boolean isDisabled();
+
+ /**
+ * Sets the disabled state of content capture.
+ *
+ * @return whether disabled state was changed.
+ */
+ abstract boolean setDisabled(boolean disabled);
/**
* Gets the id used to identify this session.
@@ -400,10 +429,11 @@
throw new IllegalArgumentException("Invalid node class: " + node.getClass());
}
- internalNotifyViewAppeared((ViewStructureImpl) node);
+ internalNotifyViewAppeared(mId, (ViewStructureImpl) node);
}
- abstract void internalNotifyViewAppeared(@NonNull ViewNode.ViewStructureImpl node);
+ abstract void internalNotifyViewAppeared(
+ int sessionId, @NonNull ViewNode.ViewStructureImpl node);
/**
* Notifies the Content Capture Service that a node has been removed from the view structure.
@@ -420,10 +450,10 @@
Objects.requireNonNull(id);
if (!isContentCaptureEnabled()) return;
- internalNotifyViewDisappeared(id);
+ internalNotifyViewDisappeared(mId, id);
}
- abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
+ abstract void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id);
/**
* Notifies the Content Capture Service that a list of nodes has appeared in the view structure.
@@ -445,12 +475,12 @@
}
}
- internalNotifyViewTreeEvent(/* started= */ true);
+ internalNotifyViewTreeEvent(mId, /* started= */ true);
for (int i = 0; i < appearedNodes.size(); i++) {
ViewStructure v = appearedNodes.get(i);
- internalNotifyViewAppeared((ViewStructureImpl) v);
+ internalNotifyViewAppeared(mId, (ViewStructureImpl) v);
}
- internalNotifyViewTreeEvent(/* started= */ false);
+ internalNotifyViewTreeEvent(mId, /* started= */ false);
}
/**
@@ -476,15 +506,15 @@
if (!isContentCaptureEnabled()) return;
if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
- internalNotifyViewTreeEvent(/* started= */ true);
+ internalNotifyViewTreeEvent(mId, /* started= */ true);
}
// TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is
// parcelized
for (long id : virtualIds) {
- internalNotifyViewDisappeared(new AutofillId(hostId, id, mId));
+ internalNotifyViewDisappeared(mId, new AutofillId(hostId, id, mId));
}
if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
- internalNotifyViewTreeEvent(/* started= */ false);
+ internalNotifyViewTreeEvent(mId, /* started= */ false);
}
}
@@ -499,10 +529,10 @@
if (!isContentCaptureEnabled()) return;
- internalNotifyViewTextChanged(id, text);
+ internalNotifyViewTextChanged(mId, id, text);
}
- abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
+ abstract void internalNotifyViewTextChanged(int sessionId, @NonNull AutofillId id,
@Nullable CharSequence text);
/**
@@ -513,13 +543,18 @@
if (!isContentCaptureEnabled()) return;
- internalNotifyViewInsetsChanged(viewInsets);
+ internalNotifyViewInsetsChanged(mId, viewInsets);
}
- abstract void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets);
+ abstract void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets);
/** @hide */
- public abstract void internalNotifyViewTreeEvent(boolean started);
+ public void notifyViewTreeEvent(boolean started) {
+ internalNotifyViewTreeEvent(mId, started);
+ }
+
+ /** @hide */
+ abstract void internalNotifyViewTreeEvent(int sessionId, boolean started);
/**
* Notifies the Content Capture Service that a session has resumed.
@@ -543,6 +578,21 @@
abstract void internalNotifySessionPaused();
+ abstract void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext);
+
+ abstract void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId);
+
+ abstract void internalNotifyContextUpdated(
+ int sessionId, @Nullable ContentCaptureContext context);
+
+ /** @hide */
+ public abstract void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds);
+
+ /** @hide */
+ public abstract void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents);
+
/**
* Creates a {@link ViewStructure} for a "standard" view.
*
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 19ba316..a90c94e 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -31,7 +31,6 @@
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
-import static android.view.contentcapture.flags.Flags.runOnBackgroundThreadEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -70,10 +69,10 @@
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+// TODO(b/309411951): Replace V2 as the only main session once the experiment is done.
/**
* Main session associated with a context.
*
@@ -82,6 +81,7 @@
*
* @hide
*/
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class MainContentCaptureSession extends ContentCaptureSession {
private static final String TAG = MainContentCaptureSession.class.getSimpleName();
@@ -97,18 +97,6 @@
*/
private static final int MSG_FLUSH = 1;
- /**
- * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
- * @hide
- */
- public static final String EXTRA_BINDER = "binder";
-
- /**
- * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
- * @hide
- */
- public static final String EXTRA_ENABLED_STATE = "enabled";
-
@NonNull
private final AtomicBoolean mDisabled = new AtomicBoolean(false);
@@ -154,15 +142,6 @@
public ComponentName mComponentName;
/**
- * Thread-safe queue of events held to be processed as a batch.
- *
- * Because it is not guaranteed that the events will be enqueued from a single thread, the
- * implementation must be thread-safe to prevent unexpected behaviour.
- */
- @NonNull
- private final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue;
-
- /**
* List of events held to be sent to the {@link ContentCaptureService} as a batch.
*
* @hide
@@ -221,14 +200,14 @@
binder = resultData.getBinder(EXTRA_BINDER);
if (binder == null) {
Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
- mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
+ mainSession.mHandler.post(() -> mainSession.resetSession(
STATE_DISABLED | STATE_INTERNAL_ERROR));
return;
}
} else {
binder = null;
}
- mainSession.runOnContentCaptureThread(() ->
+ mainSession.mHandler.post(() ->
mainSession.onSessionStarted(resultCode, binder));
}
}
@@ -249,39 +228,27 @@
mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
mSessionStateReceiver = new SessionStateReceiver(this);
-
- mEventProcessQueue = new ConcurrentLinkedQueue<>();
}
@Override
- MainContentCaptureSession getMainCaptureSession() {
+ ContentCaptureSession getMainCaptureSession() {
return this;
}
@Override
ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
- notifyChildSessionStarted(mId, child.mId, clientContext);
+ internalNotifyChildSessionStarted(mId, child.mId, clientContext);
return child;
}
/**
* Starts this session.
*/
+ @Override
void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
@NonNull ComponentName component, int flags) {
- if (runOnBackgroundThreadEnabled()) {
- runOnContentCaptureThread(
- () -> startImpl(token, shareableActivityToken, component, flags));
- } else {
- // Preserve the control arm behaviour.
- startImpl(token, shareableActivityToken, component, flags);
- }
- }
-
- private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
- @NonNull ComponentName component, int flags) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (!isContentCaptureEnabled()) return;
if (sVerbose) {
@@ -315,15 +282,17 @@
Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
}
}
+
@Override
void onDestroy() {
- clearAndRunOnContentCaptureThread(() -> {
+ mHandler.removeMessages(MSG_FLUSH);
+ mHandler.post(() -> {
try {
flush(FLUSH_REASON_SESSION_FINISHED);
} finally {
destroySession();
}
- }, MSG_FLUSH);
+ });
}
/**
@@ -336,7 +305,7 @@
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void onSessionStarted(int resultCode, @Nullable IBinder binder) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (binder != null) {
mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
mDirectServiceVulture = () -> {
@@ -385,7 +354,7 @@
}
private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
final int eventType = event.getType();
if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
@@ -429,14 +398,14 @@
}
private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (mContentProtectionEventProcessor != null) {
mContentProtectionEventProcessor.processEvent(event);
}
}
private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
final int eventType = event.getType();
final int maxBufferSize = mManager.mOptions.maxBufferSize;
if (mEvents == null) {
@@ -571,12 +540,12 @@
}
private boolean hasStarted() {
- checkOnContentCaptureThread();
+ checkOnUiThread();
return mState != UNKNOWN_STATE;
}
private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (sVerbose) {
Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
+ ", checkExisting=" + checkExisting);
@@ -617,11 +586,12 @@
+ flushFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
}
// Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
- mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
+ mHandler.postDelayed(() ->
+ flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
}
private void flushIfNeeded(@FlushReason int reason) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (mEvents == null || mEvents.isEmpty()) {
if (sVerbose) Log.v(TAG, "Nothing to flush");
return;
@@ -633,16 +603,7 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@Override
public void flush(@FlushReason int reason) {
- if (runOnBackgroundThreadEnabled()) {
- runOnContentCaptureThread(() -> flushImpl(reason));
- } else {
- // Preserve the control arm behaviour.
- flushImpl(reason);
- }
- }
-
- private void flushImpl(@FlushReason int reason) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (mEvents == null || mEvents.size() == 0) {
if (sVerbose) {
Log.v(TAG, "Don't flush for empty event buffer.");
@@ -703,7 +664,7 @@
@Override
public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
- notifyContextUpdated(mId, context);
+ internalNotifyContextUpdated(mId, context);
}
/**
@@ -711,7 +672,7 @@
*/
@NonNull
private ParceledListSlice<ContentCaptureEvent> clearEvents() {
- checkOnContentCaptureThread();
+ checkOnUiThread();
// NOTE: we must save a reference to the current mEvents and then set it to to null,
// otherwise clearing it would clear it in the receiving side if the service is also local.
if (mEvents == null) {
@@ -726,7 +687,7 @@
/** hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void destroySession() {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (sDebug) {
Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
@@ -746,9 +707,6 @@
}
mDirectServiceInterface = null;
mContentProtectionEventProcessor = null;
- if (runOnBackgroundThreadEnabled()) {
- mEventProcessQueue.clear();
- }
}
// TODO(b/122454205): once we support multiple sessions, we might need to move some of these
@@ -756,7 +714,7 @@
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void resetSession(int newState) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
if (sVerbose) {
Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
+ getStateAsString(mState) + " to " + getStateAsString(newState));
@@ -781,91 +739,22 @@
}
@Override
- void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
- notifyViewAppeared(mId, node);
- }
-
- @Override
- void internalNotifyViewDisappeared(@NonNull AutofillId id) {
- notifyViewDisappeared(mId, id);
- }
-
- @Override
- void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
- notifyViewTextChanged(mId, id, text);
- }
-
- @Override
- void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
- notifyViewInsetsChanged(mId, viewInsets);
- }
-
- @Override
- public void internalNotifyViewTreeEvent(boolean started) {
- notifyViewTreeEvent(mId, started);
- }
-
- @Override
- public void internalNotifySessionResumed() {
- notifySessionResumed(mId);
- }
-
- @Override
- public void internalNotifySessionPaused() {
- notifySessionPaused(mId);
- }
-
- @Override
- boolean isContentCaptureEnabled() {
- return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
- }
-
- // Called by ContentCaptureManager.isContentCaptureEnabled
- boolean isDisabled() {
- return mDisabled.get();
- }
-
- /**
- * Sets the disabled state of content capture.
- *
- * @return whether disabled state was changed.
- */
- boolean setDisabled(boolean disabled) {
- return mDisabled.compareAndSet(!disabled, disabled);
- }
-
- // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
- // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
- // change should also get get rid of the "internalNotifyXXXX" methods above
- void notifyChildSessionStarted(int parentSessionId, int childSessionId,
- @NonNull ContentCaptureContext clientContext) {
- final ContentCaptureEvent event =
- new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
- .setParentSessionId(parentSessionId)
- .setClientContext(clientContext);
- enqueueEvent(event, FORCE_FLUSH);
- }
-
- void notifyChildSessionFinished(int parentSessionId, int childSessionId) {
- final ContentCaptureEvent event =
- new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
- .setParentSessionId(parentSessionId);
- enqueueEvent(event, FORCE_FLUSH);
- }
-
- void notifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+ void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
.setViewNode(node.mNode);
- enqueueEvent(event);
+ mHandler.post(() -> sendEvent(event));
}
- void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+ @Override
+ void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
.setAutofillId(id);
- enqueueEvent(event);
+ mHandler.post(() -> sendEvent(event));
}
- void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+ @Override
+ void internalNotifyViewTextChanged(
+ int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
// Since the same CharSequence instance may be reused in the TextView, we need to make
// a copy of its content so that its value will not be changed by subsequent updates
// in the TextView.
@@ -891,113 +780,108 @@
.setAutofillId(id).setText(eventText)
.setComposingIndex(composingStart, composingEnd)
.setSelectionIndex(startIndex, endIndex);
- enqueueEvent(event);
+ mHandler.post(() -> sendEvent(event));
}
- void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+ @Override
+ void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
final ContentCaptureEvent event =
new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
.setInsets(viewInsets);
- enqueueEvent(event);
+ mHandler.post(() -> sendEvent(event));
}
- void notifyViewTreeEvent(int sessionId, boolean started) {
+ @Override
+ public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type);
- enqueueEvent(event, forceFlush);
+ mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
}
- void notifySessionResumed(int sessionId) {
- final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED);
- enqueueEvent(event, FORCE_FLUSH);
+ @Override
+ public void internalNotifySessionResumed() {
+ final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_RESUMED);
+ mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
}
- void notifySessionPaused(int sessionId) {
- final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED);
- enqueueEvent(event, FORCE_FLUSH);
+ @Override
+ public void internalNotifySessionPaused() {
+ final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_PAUSED);
+ mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
}
- void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+ @Override
+ boolean isContentCaptureEnabled() {
+ return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
+ }
+
+ @Override
+ boolean isDisabled() {
+ return mDisabled.get();
+ }
+
+ @Override
+ boolean setDisabled(boolean disabled) {
+ return mDisabled.compareAndSet(!disabled, disabled);
+ }
+
+ @Override
+ void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+ .setParentSessionId(parentSessionId)
+ .setClientContext(clientContext);
+ mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+ }
+
+ @Override
+ void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+ .setParentSessionId(parentSessionId);
+ mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+ }
+
+ @Override
+ void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
.setClientContext(context);
- enqueueEvent(event, FORCE_FLUSH);
+ mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
}
- /** public because is also used by ViewRootImpl */
+ @Override
public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
final ContentCaptureEvent event =
new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
.setBounds(bounds);
- enqueueEvent(event);
+ mHandler.post(() -> sendEvent(event));
}
- private List<ContentCaptureEvent> clearBufferEvents() {
- final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
- ContentCaptureEvent event;
- while ((event = mEventProcessQueue.poll()) != null) {
- bufferEvents.add(event);
- }
- return bufferEvents;
- }
-
- private void enqueueEvent(@NonNull final ContentCaptureEvent event) {
- enqueueEvent(event, /* forceFlush */ false);
- }
-
- /**
- * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise,
- * clear the buffer events then starting sending out current event.
- */
- private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
- if (runOnBackgroundThreadEnabled()) {
- if (forceFlush) {
- // The buffer events are cleared in the same thread first to prevent new events
- // being added during the time of context switch. This would disrupt the sequence
- // of events.
- final List<ContentCaptureEvent> batchEvents = clearBufferEvents();
- runOnContentCaptureThread(() -> {
- for (int i = 0; i < batchEvents.size(); i++) {
- sendEvent(batchEvents.get(i));
- }
- sendEvent(event, /* forceFlush= */ true);
- });
- } else {
- mEventProcessQueue.offer(event);
- }
- } else {
- mHandler.post(() -> sendEvent(event, forceFlush));
- }
- }
-
- /** public because is also used by ViewRootImpl */
+ @Override
public void notifyContentCaptureEvents(
@NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
- if (runOnBackgroundThreadEnabled()) {
- runOnContentCaptureThread(() -> notifyContentCaptureEventsImpl(contentCaptureEvents));
- } else {
- // Preserve the control arm behaviour.
- notifyContentCaptureEventsImpl(contentCaptureEvents);
- }
+ notifyContentCaptureEventsImpl(contentCaptureEvents);
}
private void notifyContentCaptureEventsImpl(
@NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
- checkOnContentCaptureThread();
+ checkOnUiThread();
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
}
for (int i = 0; i < contentCaptureEvents.size(); i++) {
int sessionId = contentCaptureEvents.keyAt(i);
- notifyViewTreeEvent(sessionId, /* started= */ true);
+ internalNotifyViewTreeEvent(sessionId, /* started= */ true);
ArrayList<Object> events = contentCaptureEvents.valueAt(i);
for_each_event: for (int j = 0; j < events.size(); j++) {
Object event = events.get(j);
if (event instanceof AutofillId) {
- notifyViewDisappeared(sessionId, (AutofillId) event);
+ internalNotifyViewDisappeared(sessionId, (AutofillId) event);
} else if (event instanceof View) {
View view = (View) event;
ContentCaptureSession session = view.getContentCaptureSession();
@@ -1015,12 +899,12 @@
view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
session.notifyViewAppeared(structure);
} else if (event instanceof Insets) {
- notifyViewInsetsChanged(sessionId, (Insets) event);
+ internalNotifyViewInsetsChanged(sessionId, (Insets) event);
} else {
Log.w(TAG, "invalid content capture event: " + event);
}
}
- notifyViewTreeEvent(sessionId, /* started= */ false);
+ internalNotifyViewTreeEvent(sessionId, /* started= */ false);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -1131,9 +1015,9 @@
* Therefore, accessing internal properties in {@link MainContentCaptureSession} should
* always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
*/
- private void checkOnContentCaptureThread() {
- final boolean onContentCaptureThread = mHandler.getLooper().isCurrentThread();
- if (!onContentCaptureThread) {
+ private void checkOnUiThread() {
+ final boolean onUiThread = mHandler.getLooper().isCurrentThread();
+ if (!onUiThread) {
mWrongThreadCount.incrementAndGet();
Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
}
@@ -1144,38 +1028,4 @@
Counter.logIncrement(
CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
}
-
- /**
- * Ensures that {@code r} will be running on the assigned thread.
- *
- * <p>This is to prevent unnecessary delegation to Handler that results in fragmented runnable.
- * </p>
- */
- private void runOnContentCaptureThread(@NonNull Runnable r) {
- if (runOnBackgroundThreadEnabled()) {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.post(r);
- } else {
- r.run();
- }
- } else {
- // Preserve the control arm behaviour to always post to the handler.
- mHandler.post(r);
- }
- }
-
- private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
- if (runOnBackgroundThreadEnabled()) {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.removeMessages(what);
- mHandler.post(r);
- } else {
- r.run();
- }
- } else {
- // Preserve the control arm behaviour to always post to the handler.
- mHandler.removeMessages(what);
- mHandler.post(r);
- }
- }
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java b/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java
new file mode 100644
index 0000000..bf1d31c
--- /dev/null
+++ b/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java
@@ -0,0 +1,1184 @@
+/*
+ * 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.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_INSETS_CHANGED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_WINDOW_BOUNDS_CHANGED;
+import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.ParceledListSlice;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.service.contentcapture.ContentCaptureService;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import android.view.contentprotection.ContentProtectionEventProcessor;
+import android.view.inputmethod.BaseInputConnection;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
+import com.android.modules.expresslog.Counter;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Main session associated with a context.
+ *
+ * <p>This is forked from {@link MainContentCaptureSession} to hold the logic of running operations
+ * in the background thread.</p>
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class MainContentCaptureSessionV2 extends ContentCaptureSession {
+
+ private static final String TAG = MainContentCaptureSession.class.getSimpleName();
+
+ private static final String CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID =
+ "content_capture.value_content_capture_wrong_thread_count";
+
+ // For readability purposes...
+ private static final boolean FORCE_FLUSH = true;
+
+ /**
+ * Handler message used to flush the buffer.
+ */
+ private static final int MSG_FLUSH = 1;
+
+ @NonNull
+ private final AtomicBoolean mDisabled = new AtomicBoolean(false);
+
+ @NonNull
+ private final ContentCaptureManager.StrippedContext mContext;
+
+ @NonNull
+ private final ContentCaptureManager mManager;
+
+ @NonNull
+ private final Handler mUiHandler;
+
+ @NonNull
+ private final Handler mContentCaptureHandler;
+
+ /**
+ * Interface to the system_server binder object - it's only used to start the session (and
+ * notify when the session is finished).
+ */
+ @NonNull
+ private final IContentCaptureManager mSystemServerInterface;
+
+ /**
+ * Direct interface to the service binder object - it's used to send the events, including the
+ * last ones (when the session is finished)
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @Nullable
+ public IContentCaptureDirectManager mDirectServiceInterface;
+
+ @Nullable
+ private DeathRecipient mDirectServiceVulture;
+
+ private int mState = UNKNOWN_STATE;
+
+ @Nullable
+ private IBinder mApplicationToken;
+ @Nullable
+ private IBinder mShareableActivityToken;
+
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @Nullable
+ public ComponentName mComponentName;
+
+ /**
+ * Thread-safe queue of events held to be processed as a batch.
+ *
+ * Because it is not guaranteed that the events will be enqueued from a single thread, the
+ * implementation must be thread-safe to prevent unexpected behaviour.
+ */
+ @NonNull
+ private final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue;
+
+ /**
+ * List of events held to be sent to the {@link ContentCaptureService} as a batch.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @Nullable
+ public ArrayList<ContentCaptureEvent> mEvents;
+
+ // Used just for debugging purposes (on dump)
+ private long mNextFlush;
+
+ /**
+ * Whether the next buffer flush is queued by a text changed event.
+ */
+ private boolean mNextFlushForTextChanged = false;
+
+ @Nullable
+ private final LocalLog mFlushHistory;
+
+ private final AtomicInteger mWrongThreadCount = new AtomicInteger(0);
+
+ /**
+ * Binder object used to update the session state.
+ */
+ @NonNull
+ private final SessionStateReceiver mSessionStateReceiver;
+
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @Nullable
+ public ContentProtectionEventProcessor mContentProtectionEventProcessor;
+
+ private static class SessionStateReceiver extends IResultReceiver.Stub {
+ private final WeakReference<MainContentCaptureSessionV2> mMainSession;
+
+ SessionStateReceiver(MainContentCaptureSessionV2 session) {
+ mMainSession = new WeakReference<>(session);
+ }
+
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ final MainContentCaptureSessionV2 mainSession = mMainSession.get();
+ if (mainSession == null) {
+ Log.w(TAG, "received result after mina session released");
+ return;
+ }
+ final IBinder binder;
+ if (resultData != null) {
+ // Change in content capture enabled.
+ final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
+ if (hasEnabled) {
+ final boolean disabled = (resultCode == RESULT_CODE_FALSE);
+ mainSession.mDisabled.set(disabled);
+ return;
+ }
+ binder = resultData.getBinder(EXTRA_BINDER);
+ if (binder == null) {
+ Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
+ mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
+ STATE_DISABLED | STATE_INTERNAL_ERROR));
+ return;
+ }
+ } else {
+ binder = null;
+ }
+ mainSession.runOnContentCaptureThread(() ->
+ mainSession.onSessionStarted(resultCode, binder));
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public MainContentCaptureSessionV2(
+ @NonNull ContentCaptureManager.StrippedContext context,
+ @NonNull ContentCaptureManager manager,
+ @NonNull Handler uiHandler,
+ @NonNull Handler contentCaptureHandler,
+ @NonNull IContentCaptureManager systemServerInterface) {
+ mContext = context;
+ mManager = manager;
+ mUiHandler = uiHandler;
+ mContentCaptureHandler = contentCaptureHandler;
+ mSystemServerInterface = systemServerInterface;
+
+ final int logHistorySize = mManager.mOptions.logHistorySize;
+ mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
+
+ mSessionStateReceiver = new SessionStateReceiver(this);
+
+ mEventProcessQueue = new ConcurrentLinkedQueue<>();
+ }
+
+ @Override
+ ContentCaptureSession getMainCaptureSession() {
+ return this;
+ }
+
+ @Override
+ ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
+ final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
+ internalNotifyChildSessionStarted(mId, child.mId, clientContext);
+ return child;
+ }
+
+ /**
+ * Starts this session.
+ */
+ @Override
+ void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
+ runOnContentCaptureThread(
+ () -> startImpl(token, shareableActivityToken, component, flags));
+ }
+
+ private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
+ checkOnContentCaptureThread();
+ if (!isContentCaptureEnabled()) return;
+
+ if (sVerbose) {
+ Log.v(TAG, "start(): token=" + token + ", comp="
+ + ComponentName.flattenToShortString(component));
+ }
+
+ if (hasStarted()) {
+ // TODO(b/122959591): make sure this is expected (and when), or use Log.w
+ if (sDebug) {
+ Log.d(TAG, "ignoring handleStartSession(" + token + "/"
+ + ComponentName.flattenToShortString(component) + " while on state "
+ + getStateAsString(mState));
+ }
+ return;
+ }
+ mState = STATE_WAITING_FOR_SERVER;
+ mApplicationToken = token;
+ mShareableActivityToken = shareableActivityToken;
+ mComponentName = component;
+
+ if (sVerbose) {
+ Log.v(TAG, "handleStartSession(): token=" + token + ", act="
+ + getDebugState() + ", id=" + mId);
+ }
+
+ try {
+ mSystemServerInterface.startSession(mApplicationToken, mShareableActivityToken,
+ component, mId, flags, mSessionStateReceiver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
+ }
+ }
+ @Override
+ void onDestroy() {
+ clearAndRunOnContentCaptureThread(() -> {
+ try {
+ flush(FLUSH_REASON_SESSION_FINISHED);
+ } finally {
+ destroySession();
+ }
+ }, MSG_FLUSH);
+ }
+
+ /**
+ * Callback from {@code system_server} after call to {@link
+ * IContentCaptureManager#startSession(IBinder, ComponentName, String, int, IResultReceiver)}.
+ *
+ * @param resultCode session state
+ * @param binder handle to {@code IContentCaptureDirectManager}
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void onSessionStarted(int resultCode, @Nullable IBinder binder) {
+ checkOnContentCaptureThread();
+ if (binder != null) {
+ mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+ mDirectServiceVulture = () -> {
+ Log.w(TAG, "Keeping session " + mId + " when service died");
+ mState = STATE_SERVICE_DIED;
+ mDisabled.set(true);
+ };
+ try {
+ binder.linkToDeath(mDirectServiceVulture, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
+ }
+ }
+
+ if (isContentProtectionEnabled()) {
+ mContentProtectionEventProcessor =
+ new ContentProtectionEventProcessor(
+ mManager.getContentProtectionEventBuffer(),
+ mContentCaptureHandler,
+ mSystemServerInterface,
+ mComponentName.getPackageName(),
+ mManager.mOptions.contentProtectionOptions);
+ } else {
+ mContentProtectionEventProcessor = null;
+ }
+
+ if ((resultCode & STATE_DISABLED) != 0) {
+ resetSession(resultCode);
+ } else {
+ mState = resultCode;
+ mDisabled.set(false);
+ // Flush any pending data immediately as buffering forced until now.
+ flushIfNeeded(FLUSH_REASON_SESSION_CONNECTED);
+ }
+ if (sVerbose) {
+ Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
+ + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+ + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void sendEvent(@NonNull ContentCaptureEvent event) {
+ sendEvent(event, /* forceFlush= */ false);
+ }
+
+ private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ checkOnContentCaptureThread();
+ final int eventType = event.getType();
+ if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
+ if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
+ && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
+ // TODO(b/120494182): comment when this could happen (dialogs?)
+ if (sVerbose) {
+ Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+ + ContentCaptureEvent.getTypeAsString(eventType)
+ + "): dropping because session not started yet");
+ }
+ return;
+ }
+ if (mDisabled.get()) {
+ // This happens when the event was queued in the handler before the sesison was ready,
+ // then handleSessionStarted() returned and set it as disabled - we need to drop it,
+ // otherwise it will keep triggering handleScheduleFlush()
+ if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
+ return;
+ }
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ if (eventType == TYPE_VIEW_TREE_APPEARING) {
+ Trace.asyncTraceBegin(
+ Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+ }
+ }
+
+ if (isContentProtectionReceiverEnabled()) {
+ sendContentProtectionEvent(event);
+ }
+ if (isContentCaptureReceiverEnabled()) {
+ sendContentCaptureEvent(event, forceFlush);
+ }
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ if (eventType == TYPE_VIEW_TREE_APPEARED) {
+ Trace.asyncTraceEnd(
+ Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+ }
+ }
+ }
+
+ private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) {
+ checkOnContentCaptureThread();
+ if (mContentProtectionEventProcessor != null) {
+ mContentProtectionEventProcessor.processEvent(event);
+ }
+ }
+
+ private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ checkOnContentCaptureThread();
+ final int eventType = event.getType();
+ final int maxBufferSize = mManager.mOptions.maxBufferSize;
+ if (mEvents == null) {
+ if (sVerbose) {
+ Log.v(TAG, "handleSendEvent(): creating buffer for " + maxBufferSize + " events");
+ }
+ mEvents = new ArrayList<>(maxBufferSize);
+ }
+
+ // Some type of events can be merged together
+ boolean addEvent = true;
+
+ if (eventType == TYPE_VIEW_TEXT_CHANGED) {
+ // We determine whether to add or merge the current event by following criteria:
+ // 1. Don't have composing span: always add.
+ // 2. Have composing span:
+ // 2.1 either last or current text is empty: add.
+ // 2.2 last event doesn't have composing span: add.
+ // Otherwise, merge.
+ final CharSequence text = event.getText();
+ final boolean hasComposingSpan = event.hasComposingSpan();
+ if (hasComposingSpan) {
+ ContentCaptureEvent lastEvent = null;
+ for (int index = mEvents.size() - 1; index >= 0; index--) {
+ final ContentCaptureEvent tmpEvent = mEvents.get(index);
+ if (event.getId().equals(tmpEvent.getId())) {
+ lastEvent = tmpEvent;
+ break;
+ }
+ }
+ if (lastEvent != null && lastEvent.hasComposingSpan()) {
+ final CharSequence lastText = lastEvent.getText();
+ final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
+ && !TextUtils.isEmpty(text);
+ boolean equalContent =
+ TextUtils.equals(lastText, text)
+ && lastEvent.hasSameComposingSpan(event)
+ && lastEvent.hasSameSelectionSpan(event);
+ if (equalContent) {
+ addEvent = false;
+ } else if (bothNonEmpty) {
+ lastEvent.mergeEvent(event);
+ addEvent = false;
+ }
+ if (!addEvent && sVerbose) {
+ Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
+ + getSanitizedString(text));
+ }
+ }
+ }
+ }
+
+ if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) {
+ final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
+ if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED
+ && event.getSessionId() == lastEvent.getSessionId()) {
+ if (sVerbose) {
+ Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session "
+ + lastEvent.getSessionId());
+ }
+ lastEvent.mergeEvent(event);
+ addEvent = false;
+ }
+ }
+
+ if (addEvent) {
+ mEvents.add(event);
+ }
+
+ // TODO: we need to change when the flush happens so that we don't flush while the
+ // composing span hasn't changed. But we might need to keep flushing the events for the
+ // non-editable views and views that don't have the composing state; otherwise some other
+ // Content Capture features may be delayed.
+
+ final int numberEvents = mEvents.size();
+
+ final boolean bufferEvent = numberEvents < maxBufferSize;
+
+ if (bufferEvent && !forceFlush) {
+ final int flushReason;
+ if (eventType == TYPE_VIEW_TEXT_CHANGED) {
+ mNextFlushForTextChanged = true;
+ flushReason = FLUSH_REASON_TEXT_CHANGE_TIMEOUT;
+ } else {
+ if (mNextFlushForTextChanged) {
+ if (sVerbose) {
+ Log.i(TAG, "Not scheduling flush because next flush is for text changed");
+ }
+ return;
+ }
+
+ flushReason = FLUSH_REASON_IDLE_TIMEOUT;
+ }
+ scheduleFlush(flushReason, /* checkExisting= */ true);
+ return;
+ }
+
+ if (mState != STATE_ACTIVE && numberEvents >= maxBufferSize) {
+ // Callback from startSession hasn't been called yet - typically happens on system
+ // apps that are started before the system service
+ // TODO(b/122959591): try to ignore session while system is not ready / boot
+ // not complete instead. Similarly, the manager service should return right away
+ // when the user does not have a service set
+ if (sDebug) {
+ Log.d(TAG, "Closing session for " + getDebugState()
+ + " after " + numberEvents + " delayed events");
+ }
+ resetSession(STATE_DISABLED | STATE_NO_RESPONSE);
+ // TODO(b/111276913): denylist activity / use special flag to indicate that
+ // when it's launched again
+ return;
+ }
+ final int flushReason;
+ switch (eventType) {
+ case ContentCaptureEvent.TYPE_SESSION_STARTED:
+ flushReason = FLUSH_REASON_SESSION_STARTED;
+ break;
+ case ContentCaptureEvent.TYPE_SESSION_FINISHED:
+ flushReason = FLUSH_REASON_SESSION_FINISHED;
+ break;
+ case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING:
+ flushReason = FLUSH_REASON_VIEW_TREE_APPEARING;
+ break;
+ case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED:
+ flushReason = FLUSH_REASON_VIEW_TREE_APPEARED;
+ break;
+ default:
+ flushReason = forceFlush ? FLUSH_REASON_FORCE_FLUSH : FLUSH_REASON_FULL;
+ }
+
+ flush(flushReason);
+ }
+
+ private boolean hasStarted() {
+ checkOnContentCaptureThread();
+ return mState != UNKNOWN_STATE;
+ }
+
+ private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
+ checkOnContentCaptureThread();
+ if (sVerbose) {
+ Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
+ + ", checkExisting=" + checkExisting);
+ }
+ if (!hasStarted()) {
+ if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
+ return;
+ }
+
+ if (mDisabled.get()) {
+ // Should not be called on this state, as handleSendEvent checks.
+ // But we rather add one if check and log than re-schedule and keep the session alive...
+ Log.e(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): should not be called "
+ + "when disabled. events=" + (mEvents == null ? null : mEvents.size()));
+ return;
+ }
+ if (checkExisting && mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
+ // "Renew" the flush message by removing the previous one
+ mContentCaptureHandler.removeMessages(MSG_FLUSH);
+ }
+
+ final int flushFrequencyMs;
+ if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) {
+ flushFrequencyMs = mManager.mOptions.textChangeFlushingFrequencyMs;
+ } else {
+ if (reason != FLUSH_REASON_IDLE_TIMEOUT) {
+ if (sDebug) {
+ Log.d(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): not a timeout "
+ + "reason because mDirectServiceInterface is not ready yet");
+ }
+ }
+ flushFrequencyMs = mManager.mOptions.idleFlushingFrequencyMs;
+ }
+
+ mNextFlush = System.currentTimeMillis() + flushFrequencyMs;
+ if (sVerbose) {
+ Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
+ + flushFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
+ }
+ // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
+ mContentCaptureHandler.postDelayed(() ->
+ flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
+ }
+
+ private void flushIfNeeded(@FlushReason int reason) {
+ checkOnContentCaptureThread();
+ if (mEvents == null || mEvents.isEmpty()) {
+ if (sVerbose) Log.v(TAG, "Nothing to flush");
+ return;
+ }
+ flush(reason);
+ }
+
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @Override
+ public void flush(@FlushReason int reason) {
+ runOnContentCaptureThread(() -> flushImpl(reason));
+ }
+
+ private void flushImpl(@FlushReason int reason) {
+ checkOnContentCaptureThread();
+ if (mEvents == null || mEvents.size() == 0) {
+ if (sVerbose) {
+ Log.v(TAG, "Don't flush for empty event buffer.");
+ }
+ return;
+ }
+
+ if (mDisabled.get()) {
+ Log.e(TAG, "handleForceFlush(" + getDebugState(reason) + "): should not be when "
+ + "disabled");
+ return;
+ }
+
+ if (!isContentCaptureReceiverEnabled()) {
+ return;
+ }
+
+ if (mDirectServiceInterface == null) {
+ if (sVerbose) {
+ Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
+ + "client not ready: " + mEvents);
+ }
+ if (!mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
+ scheduleFlush(reason, /* checkExisting= */ false);
+ }
+ return;
+ }
+
+ mNextFlushForTextChanged = false;
+
+ final int numberEvents = mEvents.size();
+ final String reasonString = getFlushReasonAsString(reason);
+
+ if (sVerbose) {
+ ContentCaptureEvent event = mEvents.get(numberEvents - 1);
+ String forceString = (reason == FLUSH_REASON_FORCE_FLUSH) ? ". The force flush event "
+ + ContentCaptureEvent.getTypeAsString(event.getType()) : "";
+ Log.v(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason)
+ + forceString);
+ }
+ if (mFlushHistory != null) {
+ // Logs reason, size, max size, idle timeout
+ final String logRecord = "r=" + reasonString + " s=" + numberEvents
+ + " m=" + mManager.mOptions.maxBufferSize
+ + " i=" + mManager.mOptions.idleFlushingFrequencyMs;
+ mFlushHistory.log(logRecord);
+ }
+ try {
+ mContentCaptureHandler.removeMessages(MSG_FLUSH);
+
+ final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
+ mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
+ + ": " + e);
+ }
+ }
+
+ @Override
+ public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+ internalNotifyContextUpdated(mId, context);
+ }
+
+ /**
+ * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+ */
+ @NonNull
+ private ParceledListSlice<ContentCaptureEvent> clearEvents() {
+ checkOnContentCaptureThread();
+ // NOTE: we must save a reference to the current mEvents and then set it to to null,
+ // otherwise clearing it would clear it in the receiving side if the service is also local.
+ if (mEvents == null) {
+ return new ParceledListSlice<>(Collections.EMPTY_LIST);
+ }
+
+ final List<ContentCaptureEvent> events = new ArrayList<>(mEvents);
+ mEvents.clear();
+ return new ParceledListSlice<>(events);
+ }
+
+ /** hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void destroySession() {
+ checkOnContentCaptureThread();
+ if (sDebug) {
+ Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ + getDebugState());
+ }
+
+ reportWrongThreadMetric();
+ try {
+ mSystemServerInterface.finishSession(mId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error destroying system-service session " + mId + " for "
+ + getDebugState() + ": " + e);
+ }
+
+ if (mDirectServiceInterface != null) {
+ mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+ }
+ mDirectServiceInterface = null;
+ mContentProtectionEventProcessor = null;
+ mEventProcessQueue.clear();
+ }
+
+ // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
+ // clearings out.
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void resetSession(int newState) {
+ checkOnContentCaptureThread();
+ if (sVerbose) {
+ Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
+ + getStateAsString(mState) + " to " + getStateAsString(newState));
+ }
+ mState = newState;
+ mDisabled.set((newState & STATE_DISABLED) != 0);
+ // TODO(b/122454205): must reset children (which currently is owned by superclass)
+ mApplicationToken = null;
+ mShareableActivityToken = null;
+ mComponentName = null;
+ mEvents = null;
+ if (mDirectServiceInterface != null) {
+ try {
+ mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "IContentCaptureDirectManager does not exist");
+ }
+ }
+ mDirectServiceInterface = null;
+ mContentProtectionEventProcessor = null;
+ mContentCaptureHandler.removeMessages(MSG_FLUSH);
+ }
+
+ @Override
+ void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+ final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
+ .setViewNode(node.mNode);
+ enqueueEvent(event);
+ }
+
+ @Override
+ void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+ final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
+ .setAutofillId(id);
+ enqueueEvent(event);
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(
+ int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+ // Since the same CharSequence instance may be reused in the TextView, we need to make
+ // a copy of its content so that its value will not be changed by subsequent updates
+ // in the TextView.
+ CharSequence trimmed = TextUtils.trimToParcelableSize(text);
+ final CharSequence eventText = trimmed != null && trimmed == text
+ ? trimmed.toString()
+ : trimmed;
+
+ final int composingStart;
+ final int composingEnd;
+ if (text instanceof Spannable) {
+ composingStart = BaseInputConnection.getComposingSpanStart((Spannable) text);
+ composingEnd = BaseInputConnection.getComposingSpanEnd((Spannable) text);
+ } else {
+ composingStart = ContentCaptureEvent.MAX_INVALID_VALUE;
+ composingEnd = ContentCaptureEvent.MAX_INVALID_VALUE;
+ }
+
+ final int startIndex = Selection.getSelectionStart(text);
+ final int endIndex = Selection.getSelectionEnd(text);
+
+ final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
+ .setAutofillId(id).setText(eventText)
+ .setComposingIndex(composingStart, composingEnd)
+ .setSelectionIndex(startIndex, endIndex);
+ enqueueEvent(event);
+ }
+
+ @Override
+ void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
+ .setInsets(viewInsets);
+ enqueueEvent(event);
+ }
+
+ @Override
+ public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
+ final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
+ final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
+ final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;
+
+ final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type);
+ enqueueEvent(event, forceFlush);
+ }
+
+ @Override
+ public void internalNotifySessionResumed() {
+ final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_RESUMED);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
+ @Override
+ public void internalNotifySessionPaused() {
+ final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_PAUSED);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
+ @Override
+ boolean isContentCaptureEnabled() {
+ return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
+ }
+
+ // Called by ContentCaptureManager.isContentCaptureEnabled
+ boolean isDisabled() {
+ return mDisabled.get();
+ }
+
+ /**
+ * Sets the disabled state of content capture.
+ *
+ * @return whether disabled state was changed.
+ */
+ boolean setDisabled(boolean disabled) {
+ return mDisabled.compareAndSet(!disabled, disabled);
+ }
+
+ @Override
+ void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+ .setParentSessionId(parentSessionId)
+ .setClientContext(clientContext);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
+ @Override
+ void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+ .setParentSessionId(parentSessionId);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
+ @Override
+ void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+ final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
+ .setClientContext(context);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
+ @Override
+ public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
+ .setBounds(bounds);
+ enqueueEvent(event);
+ }
+
+ private List<ContentCaptureEvent> clearBufferEvents() {
+ final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
+ ContentCaptureEvent event;
+ while ((event = mEventProcessQueue.poll()) != null) {
+ bufferEvents.add(event);
+ }
+ return bufferEvents;
+ }
+
+ private void enqueueEvent(@NonNull final ContentCaptureEvent event) {
+ enqueueEvent(event, /* forceFlush */ false);
+ }
+
+ /**
+ * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise,
+ * clear the buffer events then starting sending out current event.
+ */
+ private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
+ if (forceFlush) {
+ // The buffer events are cleared in the same thread first to prevent new events
+ // being added during the time of context switch. This would disrupt the sequence
+ // of events.
+ final List<ContentCaptureEvent> batchEvents = clearBufferEvents();
+ runOnContentCaptureThread(() -> {
+ for (int i = 0; i < batchEvents.size(); i++) {
+ sendEvent(batchEvents.get(i));
+ }
+ sendEvent(event, /* forceFlush= */ true);
+ });
+ } else {
+ mEventProcessQueue.offer(event);
+ }
+ }
+
+ @Override
+ public void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+ runOnUiThread(() -> {
+ prepareViewStructures(contentCaptureEvents);
+ runOnContentCaptureThread(() ->
+ notifyContentCaptureEventsImpl(contentCaptureEvents));
+ });
+ }
+
+ /**
+ * Traverse events and pre-process {@link View} events to {@link ViewStructureSession} events.
+ * If a {@link View} event is invalid, an empty {@link ViewStructureSession} will still be
+ * provided.
+ */
+ private void prepareViewStructures(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+ for (int i = 0; i < contentCaptureEvents.size(); i++) {
+ int sessionId = contentCaptureEvents.keyAt(i);
+ ArrayList<Object> events = contentCaptureEvents.valueAt(i);
+ for_each_event: for (int j = 0; j < events.size(); j++) {
+ Object event = events.get(j);
+ if (event instanceof View) {
+ View view = (View) event;
+ ContentCaptureSession session = view.getContentCaptureSession();
+ ViewStructureSession structureSession = new ViewStructureSession();
+
+ // Replace the View event with ViewStructureSession no matter the data is
+ // available or not. This is to ensure the sequence of the events are still
+ // the same. Calls to notifyViewAppeared will check the availability later.
+ events.set(j, structureSession);
+ if (session == null) {
+ Log.w(TAG, "no content capture session on view: " + view);
+ continue for_each_event;
+ }
+ int actualId = session.getId();
+ if (actualId != sessionId) {
+ Log.w(TAG, "content capture session mismatch for view (" + view
+ + "): was " + sessionId + " before, it's " + actualId + " now");
+ continue for_each_event;
+ }
+ ViewStructure structure = session.newViewStructure(view);
+ view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
+
+ structureSession.setSession(session);
+ structureSession.setStructure(structure);
+ }
+ }
+ }
+ }
+
+ private void notifyContentCaptureEventsImpl(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+ checkOnContentCaptureThread();
+ try {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+ }
+ for (int i = 0; i < contentCaptureEvents.size(); i++) {
+ int sessionId = contentCaptureEvents.keyAt(i);
+ internalNotifyViewTreeEvent(sessionId, /* started= */ true);
+ ArrayList<Object> events = contentCaptureEvents.valueAt(i);
+ for_each_event: for (int j = 0; j < events.size(); j++) {
+ Object event = events.get(j);
+ if (event instanceof AutofillId) {
+ internalNotifyViewDisappeared(sessionId, (AutofillId) event);
+ } else if (event instanceof ViewStructureSession viewStructureSession) {
+ viewStructureSession.notifyViewAppeared();
+ } else if (event instanceof Insets) {
+ internalNotifyViewInsetsChanged(sessionId, (Insets) event);
+ } else {
+ Log.w(TAG, "invalid content capture event: " + event);
+ }
+ }
+ internalNotifyViewTreeEvent(sessionId, /* started= */ false);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
+ @Override
+ void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dump(prefix, pw);
+
+ pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
+ if (mDirectServiceInterface != null) {
+ pw.print(prefix); pw.print("mDirectServiceInterface: ");
+ pw.println(mDirectServiceInterface);
+ }
+ pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
+ pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+ pw.print(prefix); pw.print("state: "); pw.println(getStateAsString(mState));
+ if (mApplicationToken != null) {
+ pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
+ }
+ if (mShareableActivityToken != null) {
+ pw.print(prefix); pw.print("sharable activity token: ");
+ pw.println(mShareableActivityToken);
+ }
+ if (mComponentName != null) {
+ pw.print(prefix); pw.print("component name: ");
+ pw.println(mComponentName.flattenToShortString());
+ }
+ if (mEvents != null && !mEvents.isEmpty()) {
+ final int numberEvents = mEvents.size();
+ pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
+ pw.print('/'); pw.println(mManager.mOptions.maxBufferSize);
+ if (sVerbose && numberEvents > 0) {
+ final String prefix3 = prefix + " ";
+ for (int i = 0; i < numberEvents; i++) {
+ final ContentCaptureEvent event = mEvents.get(i);
+ pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+ pw.println();
+ }
+ }
+ pw.print(prefix); pw.print("mNextFlushForTextChanged: ");
+ pw.println(mNextFlushForTextChanged);
+ pw.print(prefix); pw.print("flush frequency: ");
+ if (mNextFlushForTextChanged) {
+ pw.println(mManager.mOptions.textChangeFlushingFrequencyMs);
+ } else {
+ pw.println(mManager.mOptions.idleFlushingFrequencyMs);
+ }
+ pw.print(prefix); pw.print("next flush: ");
+ TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw);
+ pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")");
+ }
+ if (mFlushHistory != null) {
+ pw.print(prefix); pw.println("flush history:");
+ mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
+ } else {
+ pw.print(prefix); pw.println("not logging flush history");
+ }
+
+ super.dump(prefix, pw);
+ }
+
+ /**
+ * Gets a string that can be used to identify the activity on logging statements.
+ */
+ private String getActivityName() {
+ return mComponentName == null
+ ? "pkg:" + mContext.getPackageName()
+ : "act:" + mComponentName.flattenToShortString();
+ }
+
+ @NonNull
+ private String getDebugState() {
+ return getActivityName() + " [state=" + getStateAsString(mState) + ", disabled="
+ + mDisabled.get() + "]";
+ }
+
+ @NonNull
+ private String getDebugState(@FlushReason int reason) {
+ return getDebugState() + ", reason=" + getFlushReasonAsString(reason);
+ }
+
+ private boolean isContentProtectionReceiverEnabled() {
+ return mManager.mOptions.contentProtectionOptions.enableReceiver;
+ }
+
+ private boolean isContentCaptureReceiverEnabled() {
+ return mManager.mOptions.enableReceiver;
+ }
+
+ private boolean isContentProtectionEnabled() {
+ // Should not be possible for mComponentName to be null here but check anyway
+ // Should not be possible for groups to be empty if receiver is enabled but check anyway
+ return mManager.mOptions.contentProtectionOptions.enableReceiver
+ && mManager.getContentProtectionEventBuffer() != null
+ && mComponentName != null
+ && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty()
+ || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty());
+ }
+
+ /**
+ * Checks that the current work is running on the assigned thread from {@code mHandler} and
+ * count the number of times running on the wrong thread.
+ *
+ * <p>It is not guaranteed that the callers always invoke function from a single thread.
+ * Therefore, accessing internal properties in {@link MainContentCaptureSession} should
+ * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
+ */
+ private void checkOnContentCaptureThread() {
+ final boolean onContentCaptureThread = mContentCaptureHandler.getLooper().isCurrentThread();
+ if (!onContentCaptureThread) {
+ mWrongThreadCount.incrementAndGet();
+ Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
+ }
+ }
+
+ /** Reports number of times running on the wrong thread. */
+ private void reportWrongThreadMetric() {
+ Counter.logIncrement(
+ CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
+ }
+
+ /**
+ * Ensures that {@code r} will be running on the assigned thread.
+ *
+ * <p>This is to prevent unnecessary delegation to Handler that results in fragmented runnable.
+ * </p>
+ */
+ private void runOnContentCaptureThread(@NonNull Runnable r) {
+ if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
+ mContentCaptureHandler.post(r);
+ } else {
+ r.run();
+ }
+ }
+
+ private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
+ if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
+ mContentCaptureHandler.removeMessages(what);
+ mContentCaptureHandler.post(r);
+ } else {
+ r.run();
+ }
+ }
+
+ private void runOnUiThread(@NonNull Runnable r) {
+ if (mUiHandler.getLooper().isCurrentThread()) {
+ r.run();
+ } else {
+ mUiHandler.post(r);
+ }
+ }
+
+ /**
+ * Holds {@link ContentCaptureSession} and related {@link ViewStructure} for processing.
+ */
+ private static final class ViewStructureSession {
+ @Nullable private ContentCaptureSession mSession;
+ @Nullable private ViewStructure mStructure;
+
+ ViewStructureSession() {}
+
+ void setSession(@Nullable ContentCaptureSession session) {
+ this.mSession = session;
+ }
+
+ void setStructure(@Nullable ViewStructure struct) {
+ this.mStructure = struct;
+ }
+
+ /**
+ * Calls {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)} if the session and
+ * the view structure are available.
+ */
+ void notifyViewAppeared() {
+ if (mSession != null && mStructure != null) {
+ mSession.notifyViewAppeared(mStructure);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index ac9ad2d..feccc6b 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2320,6 +2320,15 @@
* @hide
*/
public boolean hideSoftInputFromView(@NonNull View view, @HideFlags int flags) {
+ final boolean isFocusedAndWindowFocused = view.hasWindowFocus() && view.isFocused();
+ synchronized (mH) {
+ if (!isFocusedAndWindowFocused && !hasServedByInputMethodLocked(view)) {
+ // Fail early if the view is not focused and not served
+ // to avoid logging many erroneous calls.
+ return false;
+ }
+ }
+
final var reason = SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_VIEW;
final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
null /* component */, Process.myUid(),
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index c7609a6..828ec26 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -44,9 +44,7 @@
static final String PROXY_BASE = "file:///cookieless_proxy/";
static final String CONTENT_BASE = "content:";
- /**
- * Cleans up (if possible) user-entered web addresses
- */
+ /** Cleans up (if possible) user-entered web addresses */
public static String guessUrl(String inUrl) {
String retVal = inUrl;
@@ -86,8 +84,12 @@
return webAddress.toString();
}
- public static String composeSearchUrl(String inQuery, String template,
- String queryPlaceHolder) {
+ /**
+ * Inserts the {@code inQuery} in the {@code template} after URL-encoding it. The encoded query
+ * will replace the {@code queryPlaceHolder}.
+ */
+ public static String composeSearchUrl(
+ String inQuery, String template, String queryPlaceHolder) {
int placeHolderIndex = template.indexOf(queryPlaceHolder);
if (placeHolderIndex < 0) {
return null;
@@ -104,8 +106,7 @@
return null;
}
- buffer.append(template.substring(
- placeHolderIndex + queryPlaceHolder.length()));
+ buffer.append(template.substring(placeHolderIndex + queryPlaceHolder.length()));
return buffer.toString();
}
@@ -123,8 +124,7 @@
byte b = url[i];
if (b == '%') {
if (url.length - i > 2) {
- b = (byte) (parseHex(url[i + 1]) * 16
- + parseHex(url[i + 2]));
+ b = (byte) (parseHex(url[i + 1]) * 16 + parseHex(url[i + 2]));
i += 2;
} else {
throw new IllegalArgumentException("Invalid format");
@@ -189,8 +189,8 @@
}
/**
- * @return {@code true} if the url is a proxy url to allow cookieless network
- * requests from a file url.
+ * @return {@code true} if the url is a proxy url to allow cookieless network requests from a
+ * file url.
* @deprecated Cookieless proxy is no longer supported.
*/
@Deprecated
@@ -202,9 +202,10 @@
* @return {@code true} if the url is a local file.
*/
public static boolean isFileUrl(String url) {
- return (null != url) && (url.startsWith(FILE_BASE) &&
- !url.startsWith(ASSET_BASE) &&
- !url.startsWith(PROXY_BASE));
+ return (null != url)
+ && (url.startsWith(FILE_BASE)
+ && !url.startsWith(ASSET_BASE)
+ && !url.startsWith(PROXY_BASE));
}
/**
@@ -232,18 +233,18 @@
* @return {@code true} if the url is an http: url.
*/
public static boolean isHttpUrl(String url) {
- return (null != url) &&
- (url.length() > 6) &&
- url.substring(0, 7).equalsIgnoreCase("http://");
+ return (null != url)
+ && (url.length() > 6)
+ && url.substring(0, 7).equalsIgnoreCase("http://");
}
/**
* @return {@code true} if the url is an https: url.
*/
public static boolean isHttpsUrl(String url) {
- return (null != url) &&
- (url.length() > 7) &&
- url.substring(0, 8).equalsIgnoreCase("https://");
+ return (null != url)
+ && (url.length() > 7)
+ && url.substring(0, 8).equalsIgnoreCase("https://");
}
/**
@@ -271,19 +272,17 @@
return false;
}
- return (isAssetUrl(url) ||
- isResourceUrl(url) ||
- isFileUrl(url) ||
- isAboutUrl(url) ||
- isHttpUrl(url) ||
- isHttpsUrl(url) ||
- isJavaScriptUrl(url) ||
- isContentUrl(url));
+ return (isAssetUrl(url)
+ || isResourceUrl(url)
+ || isFileUrl(url)
+ || isAboutUrl(url)
+ || isHttpUrl(url)
+ || isHttpsUrl(url)
+ || isJavaScriptUrl(url)
+ || isContentUrl(url));
}
- /**
- * Strips the url of the anchor.
- */
+ /** Strips the url of the anchor. */
public static String stripAnchor(String url) {
int anchorIndex = url.indexOf('#');
if (anchorIndex != -1) {
@@ -293,19 +292,16 @@
}
/**
- * Guesses canonical filename that a download would have, using
- * the URL and contentDisposition. File extension, if not defined,
- * is added based on the mimetype
+ * Guesses canonical filename that a download would have, using the URL and contentDisposition.
+ * File extension, if not defined, is added based on the mimetype
+ *
* @param url Url to the content
* @param contentDisposition Content-Disposition HTTP header or {@code null}
* @param mimeType Mime-type of the content or {@code null}
- *
* @return suggested filename
*/
public static final String guessFileName(
- String url,
- @Nullable String contentDisposition,
- @Nullable String mimeType) {
+ String url, @Nullable String contentDisposition, @Nullable String mimeType) {
String filename = null;
String extension = null;
@@ -369,8 +365,9 @@
// Compare the last segment of the extension against the mime type.
// If there's a mismatch, discard the entire extension.
int lastDotIndex = filename.lastIndexOf('.');
- String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- filename.substring(lastDotIndex + 1));
+ String typeFromExt =
+ MimeTypeMap.getSingleton()
+ .getMimeTypeFromExtension(filename.substring(lastDotIndex + 1));
if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
if (extension != null) {
@@ -389,17 +386,17 @@
/** Regex used to parse content-disposition headers */
private static final Pattern CONTENT_DISPOSITION_PATTERN =
- Pattern.compile("attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
- Pattern.CASE_INSENSITIVE);
+ Pattern.compile(
+ "attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$",
+ Pattern.CASE_INSENSITIVE);
/**
- * Parse the Content-Disposition HTTP Header. The format of the header
- * is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
- * This header provides a filename for content that is going to be
- * downloaded to the file system. We only support the attachment type.
- * Note that RFC 2616 specifies the filename value must be double-quoted.
- * Unfortunately some servers do not quote the value so to maintain
- * consistent behaviour with other browsers, we allow unquoted values too.
+ * Parse the Content-Disposition HTTP Header. The format of the header is defined here:
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for
+ * content that is going to be downloaded to the file system. We only support the attachment
+ * type. Note that RFC 2616 specifies the filename value must be double-quoted. Unfortunately
+ * some servers do not quote the value so to maintain consistent behaviour with other browsers,
+ * we allow unquoted values too.
*/
@UnsupportedAppUsage
static String parseContentDisposition(String contentDisposition) {
@@ -409,7 +406,7 @@
return m.group(2);
}
} catch (IllegalStateException ex) {
- // This function is defined as returning null when it can't parse the header
+ // This function is defined as returning null when it can't parse the header
}
return null;
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 14c5348..d12eda3 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1203,7 +1203,11 @@
* changes to this setting after that point.
*
* @param flag {@code true} if the WebView should use the database storage API
+ * @deprecated WebSQL is deprecated and this method will become a no-op on all
+ * Android versions once support is removed in Chromium. See
+ * https://developer.chrome.com/blog/deprecating-web-sql for more information.
*/
+ @Deprecated
public abstract void setDatabaseEnabled(boolean flag);
/**
@@ -1236,7 +1240,11 @@
*
* @return {@code true} if the database storage API is enabled
* @see #setDatabaseEnabled
+ * @deprecated WebSQL is deprecated and this method will become a no-op on all
+ * Android versions once support is removed in Chromium. See
+ * https://developer.chrome.com/blog/deprecating-web-sql for more information.
*/
+ @Deprecated
public abstract boolean getDatabaseEnabled();
/**
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index c475723..7cda3a3 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -36,6 +36,7 @@
private final float mProgress;
private final float mVelocityX;
private final float mVelocityY;
+ private final boolean mTriggerBack;
@BackEvent.SwipeEdge
private final int mSwipeEdge;
@@ -54,6 +55,7 @@
* Value in pixels/second. {@link Float#NaN} if was not computed.
* @param velocityY Y velocity computed from the touch point of this event.
* Value in pixels/second. {@link Float#NaN} if was not computed.
+ * @param triggerBack Indicates whether the back arrow is in the triggered state or not
* @param swipeEdge Indicates which edge the swipe starts from.
* @param departingAnimationTarget The remote animation target of the departing
* application window.
@@ -64,6 +66,7 @@
float progress,
float velocityX,
float velocityY,
+ boolean triggerBack,
@BackEvent.SwipeEdge int swipeEdge,
@Nullable RemoteAnimationTarget departingAnimationTarget) {
mTouchX = touchX;
@@ -71,6 +74,7 @@
mProgress = progress;
mVelocityX = velocityX;
mVelocityY = velocityY;
+ mTriggerBack = triggerBack;
mSwipeEdge = swipeEdge;
mDepartingAnimationTarget = departingAnimationTarget;
}
@@ -81,6 +85,7 @@
mProgress = in.readFloat();
mVelocityX = in.readFloat();
mVelocityY = in.readFloat();
+ mTriggerBack = in.readBoolean();
mSwipeEdge = in.readInt();
mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
}
@@ -110,6 +115,7 @@
dest.writeFloat(mProgress);
dest.writeFloat(mVelocityX);
dest.writeFloat(mVelocityY);
+ dest.writeBoolean(mTriggerBack);
dest.writeInt(mSwipeEdge);
dest.writeTypedObject(mDepartingAnimationTarget, flags);
}
@@ -157,6 +163,15 @@
}
/**
+ * Returns whether the back arrow is in the triggered state or not
+ *
+ * @return boolean indicating whether the back arrow is in the triggered state or not
+ */
+ public boolean getTriggerBack() {
+ return mTriggerBack;
+ }
+
+ /**
* Returns the screen edge that the swipe starts from.
*/
@BackEvent.SwipeEdge
@@ -182,6 +197,7 @@
+ ", mProgress=" + mProgress
+ ", mVelocityX=" + mVelocityX
+ ", mVelocityY=" + mVelocityY
+ + ", mTriggerBack=" + mTriggerBack
+ ", mSwipeEdge" + mSwipeEdge
+ ", mDepartingAnimationTarget" + mDepartingAnimationTarget
+ "}";
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 5bfa3d7..7c9340e 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -165,6 +165,9 @@
public static final int FLAGS_IS_NON_APP_WINDOW =
FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD | FLAG_IS_SYSTEM_WINDOW;
+ /** The change will not participate in the animation. */
+ public static final int FLAGS_IS_OCCLUDED_NO_ANIMATION = FLAG_IS_OCCLUDED | FLAG_NO_ANIMATION;
+
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
FLAG_NONE,
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 52ad49a..216acdc 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -66,4 +66,12 @@
description: "Predictive back for system animations"
bug: "309545085"
is_fixed_read_only: true
+}
+
+flag {
+ name: "activity_snapshot_by_default"
+ namespace: "systemui"
+ description: "Enable record activity snapshot by default"
+ bug: "259497289"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 933cc49..59d7b0e 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -2,13 +2,6 @@
# Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
-flag {
- namespace: "windowing_sdk"
- name: "sync_window_config_update_flag"
- description: "Whether the feature to sync different window-related config updates is enabled"
- bug: "260873529"
-}
-
# Using a fixed read only flag because there are ClientTransaction scheduling before
# WindowManagerService creation.
flag {
@@ -35,13 +28,6 @@
flag {
namespace: "windowing_sdk"
- name: "window_state_resize_item_flag"
- description: "Whether to dispatch window resize through ClientTransaction is enabled"
- bug: "301870955"
-}
-
-flag {
- namespace: "windowing_sdk"
name: "fullscreen_dim_flag"
description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
bug: "253533308"
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7534d29..7dcbbea 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -249,6 +249,7 @@
private UserHandle mCloneProfileUserHandle;
private UserHandle mTabOwnerUserHandleForLaunch;
+ private UserHandle mPrivateProfileUserHandle;
protected final LatencyTracker mLatencyTracker = getLatencyTracker();
@@ -441,6 +442,7 @@
mPersonalProfileUserHandle = fetchPersonalProfileUserHandle();
mWorkProfileUserHandle = fetchWorkProfileUserProfile();
mCloneProfileUserHandle = fetchCloneProfileUserHandle();
+ mPrivateProfileUserHandle = fetchPrivateProfileUserHandle();
mTabOwnerUserHandleForLaunch = fetchTabOwnerUserHandleForLaunch();
// The last argument of createResolverListAdapter is whether to do special handling
@@ -648,7 +650,8 @@
initialIntents,
rList,
filterLastUsed,
- /* userHandle */ getPersonalProfileUserHandle());
+ getPersonalProfileUserHandle());
+
QuietModeManager quietModeManager = createQuietModeManager();
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
@@ -747,6 +750,9 @@
}
protected UserHandle getPersonalProfileUserHandle() {
+ if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()){
+ return mPrivateProfileUserHandle;
+ }
return mPersonalProfileUserHandle;
}
protected @Nullable UserHandle getWorkProfileUserHandle() {
@@ -761,6 +767,10 @@
return mTabOwnerUserHandleForLaunch;
}
+ protected UserHandle getPrivateProfileUserHandle() {
+ return mPrivateProfileUserHandle;
+ }
+
protected UserHandle fetchPersonalProfileUserHandle() {
// ActivityManager.getCurrentUser() refers to the current Foreground user. When clone/work
// profile is active, we always make the personal tab from the foreground user.
@@ -795,12 +805,28 @@
return mCloneProfileUserHandle;
}
+ protected @Nullable UserHandle fetchPrivateProfileUserHandle() {
+ mPrivateProfileUserHandle = null;
+ UserManager userManager = getSystemService(UserManager.class);
+ for (final UserInfo userInfo :
+ userManager.getProfiles(mPersonalProfileUserHandle.getIdentifier())) {
+ if (userInfo.isPrivateProfile()) {
+ mPrivateProfileUserHandle = userInfo.getUserHandle();
+ break;
+ }
+ }
+ return mPrivateProfileUserHandle;
+ }
+
private UserHandle fetchTabOwnerUserHandleForLaunch() {
- // If we are in work profile's process, return WorkProfile user as owner, otherwise we
- // always return PersonalProfile user as owner
- return UserHandle.of(UserHandle.myUserId()).equals(getWorkProfileUserHandle())
- ? getWorkProfileUserHandle()
- : getPersonalProfileUserHandle();
+ // If we are in work or private profile's process, return WorkProfile/PrivateProfile user
+ // as owner, otherwise we always return PersonalProfile user as owner
+ if (UserHandle.of(UserHandle.myUserId()).equals(getWorkProfileUserHandle())) {
+ return getWorkProfileUserHandle();
+ } else if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()) {
+ return getPrivateProfileUserHandle();
+ }
+ return getPersonalProfileUserHandle();
}
private boolean hasWorkProfile() {
@@ -816,7 +842,15 @@
&& (UserHandle.myUserId() == getCloneProfileUserHandle().getIdentifier());
}
+ protected final boolean isLaunchedAsPrivateProfile() {
+ return getPrivateProfileUserHandle() != null
+ && (UserHandle.myUserId() == getPrivateProfileUserHandle().getIdentifier());
+ }
+
protected boolean shouldShowTabs() {
+ if (privateSpaceEnabled() && isLaunchedAsPrivateProfile()) {
+ return false;
+ }
return hasWorkProfile() && ENABLE_TABBED_VIEW;
}
@@ -2619,6 +2653,11 @@
return resolveInfo.userHandle;
}
+ private boolean privateSpaceEnabled() {
+ return mIsIntentPicker && android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.allowResolverSheetForPrivateSpace();
+ }
+
/**
* An a11y delegate that expands resolver drawer when gesture navigation reaches a partially
* invisible target in the list.
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index c89cfc4..5705b7e 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -37,6 +37,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
+import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -63,6 +64,8 @@
PackageMonitorCallback mPackageMonitorCallback;
+ private Executor mExecutor;
+
@UnsupportedAppUsage
public PackageMonitor() {
final boolean isCore = UserHandle.isCore(android.os.Process.myUid());
@@ -106,8 +109,8 @@
if (mPackageMonitorCallback == null) {
PackageManager pm = mRegisteredContext.getPackageManager();
if (pm != null) {
- mPackageMonitorCallback = new PackageMonitorCallback(this,
- new HandlerExecutor(mRegisteredHandler));
+ mExecutor = new HandlerExecutor(mRegisteredHandler);
+ mPackageMonitorCallback = new PackageMonitorCallback(this);
int userId = user != null ? user.getIdentifier() : mRegisteredContext.getUserId();
pm.registerPackageMonitorCallback(mPackageMonitorCallback, userId);
}
@@ -131,6 +134,7 @@
}
mPackageMonitorCallback = null;
mRegisteredContext = null;
+ mExecutor = null;
}
public void onBeginPackageChanges() {
@@ -362,6 +366,13 @@
doHandlePackageEvent(intent);
}
+
+ private void postHandlePackageEvent(Intent intent) {
+ if (mExecutor != null) {
+ mExecutor.execute(() -> doHandlePackageEvent(intent));
+ }
+ }
+
/**
* Handle the package related event
* @param intent the intent that contains package related event information
@@ -516,13 +527,10 @@
}
private static final class PackageMonitorCallback extends IRemoteCallback.Stub {
+ private final WeakReference<PackageMonitor> mMonitorWeakReference;
- private final PackageMonitor mPackageMonitor;
- private final Executor mExecutor;
-
- PackageMonitorCallback(PackageMonitor monitor, Executor executor) {
- mPackageMonitor = monitor;
- mExecutor = executor;
+ PackageMonitorCallback(PackageMonitor monitor) {
+ mMonitorWeakReference = new WeakReference<>(monitor);
}
@Override
@@ -537,7 +545,10 @@
Log.w(TAG, "No intent is set for PackageMonitorCallback");
return;
}
- mExecutor.execute(() -> mPackageMonitor.doHandlePackageEvent(intent));
+ PackageMonitor monitor = mMonitorWeakReference.get();
+ if (monitor != null) {
+ monitor.postHandlePackageEvent(intent);
+ }
}
}
}
diff --git a/core/java/com/android/internal/pm/parsing/IPackageCacher.java b/core/java/com/android/internal/pm/parsing/IPackageCacher.java
new file mode 100644
index 0000000..3e01730
--- /dev/null
+++ b/core/java/com/android/internal/pm/parsing/IPackageCacher.java
@@ -0,0 +1,35 @@
+/*
+ * 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.internal.pm.parsing;
+
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+
+import java.io.File;
+
+/** @hide */
+public interface IPackageCacher {
+
+ /**
+ * Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
+ * or {@code null} if no cached result exists.
+ */
+ ParsedPackage getCachedResult(File packageFile, int flags);
+
+ /**
+ * Caches the parse result for {@code packageFile} with flags {@code flags}.
+ */
+ void cacheResult(File packageFile, int flags, ParsedPackage parsed);
+}
diff --git a/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java b/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java
new file mode 100644
index 0000000..983658a
--- /dev/null
+++ b/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java
@@ -0,0 +1,654 @@
+/*
+ * 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.internal.pm.parsing;
+
+import static com.android.internal.pm.pkg.SEInfoUtil.COMPLETE_STR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.Attribution;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FallbackCategoryProvider;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningInfo;
+import android.os.Debug;
+import android.os.PatternMatcher;
+import android.os.UserHandle;
+import android.util.DebugUtils;
+import android.util.Slog;
+
+import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
+import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
+import com.android.internal.pm.parsing.pkg.PackageImpl;
+import com.android.internal.pm.pkg.component.ComponentParseUtils;
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedAttribution;
+import com.android.internal.pm.pkg.component.ParsedComponent;
+import com.android.internal.pm.pkg.component.ParsedInstrumentation;
+import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
+import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import java.util.List;
+
+/**
+ * Method that use a {@link AndroidPackage} to generate a {@link PackageInfo} though
+ * the given {@link PackageManager.PackageInfoFlags}
+ * @hide
+ **/
+// TODO(b/317215254): refactor coped code from PackageInfoUtils
+public class PackageInfoCommonUtils {
+
+ private static final String TAG = ParsingUtils.TAG;
+ private static final boolean DEBUG = false;
+
+ /**
+ * Generates a {@link PackageInfo} from the given {@link AndroidPackage}
+ */
+ @Nullable
+ public static PackageInfo generate(@Nullable AndroidPackage pkg,
+ @PackageManager.PackageInfoFlagsBits long flags, int userId) {
+ if (pkg == null) {
+ return null;
+ }
+ ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, userId);
+
+ PackageInfo info = new PackageInfo();
+ info.packageName = pkg.getPackageName();
+ info.splitNames = pkg.getSplitNames();
+ info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
+ info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
+ info.baseRevisionCode = pkg.getBaseRevisionCode();
+ info.splitRevisionCodes = pkg.getSplitRevisionCodes();
+ info.versionName = pkg.getVersionName();
+ if (!pkg.isLeavingSharedUser()) {
+ info.sharedUserId = pkg.getSharedUserId();
+ info.sharedUserLabel = pkg.getSharedUserLabelResourceId();
+ }
+ info.applicationInfo = applicationInfo;
+ info.installLocation = pkg.getInstallLocation();
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ || (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ info.requiredForAllUsers = pkg.isRequiredForAllUsers();
+ }
+ info.restrictedAccountType = pkg.getRestrictedAccountType();
+ info.requiredAccountType = pkg.getRequiredAccountType();
+ info.overlayTarget = pkg.getOverlayTarget();
+ info.targetOverlayableName = pkg.getOverlayTargetOverlayableName();
+ info.overlayCategory = pkg.getOverlayCategory();
+ info.overlayPriority = pkg.getOverlayPriority();
+ info.mOverlayIsStatic = pkg.isOverlayIsStatic();
+ info.compileSdkVersion = pkg.getCompileSdkVersion();
+ info.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+ info.isStub = pkg.isStub();
+ info.coreApp = pkg.isCoreApp();
+ info.isApex = pkg.isApex();
+
+ if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
+ int size = pkg.getConfigPreferences().size();
+ if (size > 0) {
+ info.configPreferences = new ConfigurationInfo[size];
+ pkg.getConfigPreferences().toArray(info.configPreferences);
+ }
+ size = pkg.getRequestedFeatures().size();
+ if (size > 0) {
+ info.reqFeatures = new FeatureInfo[size];
+ pkg.getRequestedFeatures().toArray(info.reqFeatures);
+ }
+ size = pkg.getFeatureGroups().size();
+ if (size > 0) {
+ info.featureGroups = new FeatureGroupInfo[size];
+ pkg.getFeatureGroups().toArray(info.featureGroups);
+ }
+ }
+ if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
+ int size = ArrayUtils.size(pkg.getPermissions());
+ if (size > 0) {
+ info.permissions = new PermissionInfo[size];
+ for (int i = 0; i < size; i++) {
+ final var permission = pkg.getPermissions().get(i);
+ final var permissionInfo = generatePermissionInfo(permission, flags);
+ info.permissions[i] = permissionInfo;
+ }
+ }
+ final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions();
+ size = usesPermissions.size();
+ if (size > 0) {
+ info.requestedPermissions = new String[size];
+ info.requestedPermissionsFlags = new int[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedUsesPermission usesPermission = usesPermissions.get(i);
+ info.requestedPermissions[i] = usesPermission.getName();
+ // The notion of required permissions is deprecated but for compatibility.
+ info.requestedPermissionsFlags[i] |=
+ PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+ if ((usesPermission.getUsesPermissionFlags()
+ & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) {
+ info.requestedPermissionsFlags[i] |=
+ PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
+ }
+ if (pkg.getImplicitPermissions().contains(info.requestedPermissions[i])) {
+ info.requestedPermissionsFlags[i] |=
+ PackageInfo.REQUESTED_PERMISSION_IMPLICIT;
+ }
+ }
+ }
+ }
+ if ((flags & PackageManager.GET_ATTRIBUTIONS_LONG) != 0) {
+ int size = ArrayUtils.size(pkg.getAttributions());
+ if (size > 0) {
+ info.attributions = new Attribution[size];
+ for (int i = 0; i < size; i++) {
+ ParsedAttribution parsedAttribution = pkg.getAttributions().get(i);
+ if (parsedAttribution != null) {
+ info.attributions[i] = new Attribution(parsedAttribution.getTag(),
+ parsedAttribution.getLabel());
+ }
+ }
+ }
+ if (pkg.isAttributionsUserVisible()) {
+ info.applicationInfo.privateFlagsExt
+ |= ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+ } else {
+ info.applicationInfo.privateFlagsExt
+ &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+ }
+ } else {
+ info.applicationInfo.privateFlagsExt
+ &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE;
+ }
+
+ final SigningDetails signingDetails = pkg.getSigningDetails();
+ // deprecated method of getting signing certificates
+ if ((flags & PackageManager.GET_SIGNATURES) != 0) {
+ if (signingDetails.hasPastSigningCertificates()) {
+ // Package has included signing certificate rotation information. Return the oldest
+ // cert so that programmatic checks keep working even if unaware of key rotation.
+ info.signatures = new Signature[1];
+ info.signatures[0] = signingDetails.getPastSigningCertificates()[0];
+ } else if (signingDetails.hasSignatures()) {
+ // otherwise keep old behavior
+ int numberOfSigs = signingDetails.getSignatures().length;
+ info.signatures = new Signature[numberOfSigs];
+ System.arraycopy(signingDetails.getSignatures(), 0, info.signatures, 0,
+ numberOfSigs);
+ }
+ }
+
+ // replacement for GET_SIGNATURES
+ if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+ if (signingDetails != SigningDetails.UNKNOWN) {
+ // only return a valid SigningInfo if there is signing information to report
+ info.signingInfo = new SigningInfo(signingDetails);
+ } else {
+ info.signingInfo = null;
+ }
+ }
+
+ if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+ final int size = pkg.getActivities().size();
+ if (size > 0) {
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedActivity a = pkg.getActivities().get(i);
+ if (isMatch(pkg, a.isDirectBootAware(), flags)) {
+ if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
+ a.getName())) {
+ continue;
+ }
+ res[num++] = generateActivityInfo(a, flags, applicationInfo);
+ }
+ }
+ info.activities = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+ final int size = pkg.getReceivers().size();
+ if (size > 0) {
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedActivity a = pkg.getReceivers().get(i);
+ if (isMatch(pkg, a.isDirectBootAware(), flags)) {
+ res[num++] = generateActivityInfo(a, flags, applicationInfo);
+ }
+ }
+ info.receivers = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_SERVICES) != 0) {
+ final int size = pkg.getServices().size();
+ if (size > 0) {
+ int num = 0;
+ final ServiceInfo[] res = new ServiceInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedService s = pkg.getServices().get(i);
+ if (isMatch(pkg, s.isDirectBootAware(), flags)) {
+ res[num++] = generateServiceInfo(s, flags, applicationInfo);
+ }
+ }
+ info.services = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+ final int size = pkg.getProviders().size();
+ if (size > 0) {
+ int num = 0;
+ final ProviderInfo[] res = new ProviderInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedProvider pr = pkg.getProviders().get(i);
+ if (isMatch(pkg, pr.isDirectBootAware(), flags)) {
+ res[num++] = generateProviderInfo(pkg, pr, flags, applicationInfo, userId);
+ }
+ }
+ info.providers = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
+ final int size = pkg.getInstrumentations().size();
+ if (size > 0) {
+ info.instrumentation = new InstrumentationInfo[size];
+ for (int i = 0; i < size; i++) {
+ info.instrumentation[i] = generateInstrumentationInfo(
+ pkg.getInstrumentations().get(i), pkg, flags, userId);
+ }
+ }
+ }
+
+ return info;
+ }
+
+ private static void updateApplicationInfo(ApplicationInfo ai, long flags) {
+ if ((flags & PackageManager.GET_META_DATA) == 0) {
+ ai.metaData = null;
+ }
+ if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) {
+ ai.sharedLibraryFiles = null;
+ ai.sharedLibraryInfos = null;
+ }
+
+ // CompatibilityMode is global state.
+ if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
+ ai.disableCompatibilityMode();
+ }
+
+ if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+ ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
+ }
+ ai.seInfoUser = COMPLETE_STR;
+ }
+
+ @Nullable
+ private static ApplicationInfo generateApplicationInfo(@NonNull AndroidPackage pkg,
+ @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId) {
+
+ // Make shallow copy so we can store the metadata/libraries safely
+ ApplicationInfo info = ((AndroidPackageHidden) pkg).toAppInfoWithoutState();
+
+ updateApplicationInfo(info, flags);
+
+ initForUser(info, pkg, userId);
+
+ info.primaryCpuAbi = AndroidPackageLegacyUtils.getRawPrimaryCpuAbi(pkg);
+ info.secondaryCpuAbi = AndroidPackageLegacyUtils.getRawSecondaryCpuAbi(pkg);
+
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ info.metaData = pkg.getMetaData();
+ }
+ if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
+ List<String> usesLibraryFiles = pkg.getUsesLibraries();
+
+ info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
+ ? null : usesLibraryFiles.toArray(new String[0]);
+ }
+
+ return info;
+ }
+
+ @Nullable
+ private static ActivityInfo generateActivityInfo(ParsedActivity a,
+ @PackageManager.ComponentInfoFlagsBits long flags,
+ @NonNull ApplicationInfo applicationInfo) {
+ if (a == null) return null;
+
+ // Make shallow copies so we can store the metadata safely
+ ActivityInfo ai = new ActivityInfo();
+ ai.targetActivity = a.getTargetActivity();
+ ai.processName = a.getProcessName();
+ ai.exported = a.isExported();
+ ai.theme = a.getTheme();
+ ai.uiOptions = a.getUiOptions();
+ ai.parentActivityName = a.getParentActivityName();
+ ai.permission = a.getPermission();
+ ai.taskAffinity = a.getTaskAffinity();
+ ai.flags = a.getFlags();
+ ai.privateFlags = a.getPrivateFlags();
+ ai.launchMode = a.getLaunchMode();
+ ai.documentLaunchMode = a.getDocumentLaunchMode();
+ ai.maxRecents = a.getMaxRecents();
+ ai.configChanges = a.getConfigChanges();
+ ai.softInputMode = a.getSoftInputMode();
+ ai.persistableMode = a.getPersistableMode();
+ ai.lockTaskLaunchMode = a.getLockTaskLaunchMode();
+ ai.screenOrientation = a.getScreenOrientation();
+ ai.resizeMode = a.getResizeMode();
+ ai.setMaxAspectRatio(a.getMaxAspectRatio());
+ ai.setMinAspectRatio(a.getMinAspectRatio());
+ ai.supportsSizeChanges = a.isSupportsSizeChanges();
+ ai.requestedVrComponent = a.getRequestedVrComponent();
+ ai.rotationAnimation = a.getRotationAnimation();
+ ai.colorMode = a.getColorMode();
+ ai.windowLayout = a.getWindowLayout();
+ ai.attributionTags = a.getAttributionTags();
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ var metaData = a.getMetaData();
+ // Backwards compatibility, coerce to null if empty
+ ai.metaData = metaData.isEmpty() ? null : metaData;
+ } else {
+ ai.metaData = null;
+ }
+ ai.applicationInfo = applicationInfo;
+ ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
+ ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
+ assignFieldsComponentInfoParsedMainComponent(ai, a);
+ return ai;
+ }
+
+ @Nullable
+ private static ServiceInfo generateServiceInfo(ParsedService s,
+ @PackageManager.ComponentInfoFlagsBits long flags,
+ @NonNull ApplicationInfo applicationInfo) {
+ if (s == null) return null;
+
+ // Make shallow copies so we can store the metadata safely
+ ServiceInfo si = new ServiceInfo();
+ si.exported = s.isExported();
+ si.flags = s.getFlags();
+ si.permission = s.getPermission();
+ si.processName = s.getProcessName();
+ si.mForegroundServiceType = s.getForegroundServiceType();
+ si.applicationInfo = applicationInfo;
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ var metaData = s.getMetaData();
+ // Backwards compatibility, coerce to null if empty
+ si.metaData = metaData.isEmpty() ? null : metaData;
+ }
+ assignFieldsComponentInfoParsedMainComponent(si, s);
+ return si;
+ }
+
+ @Nullable
+ private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
+ @PackageManager.ComponentInfoFlagsBits long flags,
+ @NonNull ApplicationInfo applicationInfo, int userId) {
+ if (p == null) return null;
+
+ if (!pkg.getPackageName().equals(applicationInfo.packageName)) {
+ Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName()
+ + " actual=" + applicationInfo.packageName);
+ applicationInfo = generateApplicationInfo(pkg, flags, userId);
+ }
+
+ // Make shallow copies so we can store the metadata safely
+ ProviderInfo pi = new ProviderInfo();
+ pi.exported = p.isExported();
+ pi.flags = p.getFlags();
+ pi.processName = p.getProcessName();
+ pi.authority = p.getAuthority();
+ pi.isSyncable = p.isSyncable();
+ pi.readPermission = p.getReadPermission();
+ pi.writePermission = p.getWritePermission();
+ pi.grantUriPermissions = p.isGrantUriPermissions();
+ pi.forceUriPermissions = p.isForceUriPermissions();
+ pi.multiprocess = p.isMultiProcess();
+ pi.initOrder = p.getInitOrder();
+ pi.uriPermissionPatterns = p.getUriPermissionPatterns().toArray(new PatternMatcher[0]);
+ pi.pathPermissions = p.getPathPermissions().toArray(new PathPermission[0]);
+ if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
+ pi.uriPermissionPatterns = null;
+ }
+ if ((flags & PackageManager.GET_META_DATA) != 0) {
+ var metaData = p.getMetaData();
+ // Backwards compatibility, coerce to null if empty
+ pi.metaData = metaData.isEmpty() ? null : metaData;
+ }
+ pi.applicationInfo = applicationInfo;
+ assignFieldsComponentInfoParsedMainComponent(pi, p);
+ return pi;
+ }
+
+ @Nullable
+ private static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
+ AndroidPackage pkg, @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
+ if (i == null) return null;
+
+ InstrumentationInfo info = new InstrumentationInfo();
+ info.targetPackage = i.getTargetPackage();
+ info.targetProcesses = i.getTargetProcesses();
+ info.handleProfiling = i.isHandleProfiling();
+ info.functionalTest = i.isFunctionalTest();
+
+ info.sourceDir = pkg.getBaseApkPath();
+ info.publicSourceDir = pkg.getBaseApkPath();
+ info.splitNames = pkg.getSplitNames();
+ info.splitSourceDirs = pkg.getSplitCodePaths().length == 0 ? null : pkg.getSplitCodePaths();
+ info.splitPublicSourceDirs = pkg.getSplitCodePaths().length == 0
+ ? null : pkg.getSplitCodePaths();
+ info.splitDependencies = pkg.getSplitDependencies().size() == 0
+ ? null : pkg.getSplitDependencies();
+
+ initForUser(info, pkg, userId);
+
+ info.primaryCpuAbi = AndroidPackageLegacyUtils.getRawPrimaryCpuAbi(pkg);
+ info.secondaryCpuAbi = AndroidPackageLegacyUtils.getRawSecondaryCpuAbi(pkg);
+ info.nativeLibraryDir = pkg.getNativeLibraryDir();
+ info.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
+
+ assignFieldsPackageItemInfoParsedComponent(info, i);
+
+ if ((flags & PackageManager.GET_META_DATA) == 0) {
+ info.metaData = null;
+ } else {
+ var metaData = i.getMetaData();
+ // Backwards compatibility, coerce to null if empty
+ info.metaData = metaData.isEmpty() ? null : metaData;
+ }
+
+ return info;
+ }
+
+ @Nullable
+ private static PermissionInfo generatePermissionInfo(ParsedPermission p,
+ @PackageManager.ComponentInfoFlagsBits long flags) {
+ // TODO(b/135203078): Remove null checks and make all usages @NonNull
+ if (p == null) return null;
+
+ PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
+
+ assignFieldsPackageItemInfoParsedComponent(pi, p);
+
+ pi.group = p.getGroup();
+ pi.requestRes = p.getRequestRes();
+ pi.protectionLevel = p.getProtectionLevel();
+ pi.descriptionRes = p.getDescriptionRes();
+ pi.flags = p.getFlags();
+ pi.knownCerts = p.getKnownCerts();
+
+ if ((flags & PackageManager.GET_META_DATA) == 0) {
+ pi.metaData = null;
+ } else {
+ var metaData = p.getMetaData();
+ // Backwards compatibility, coerce to null if empty
+ pi.metaData = metaData.isEmpty() ? null : metaData;
+ }
+ return pi;
+ }
+
+ private static void assignFieldsComponentInfoParsedMainComponent(
+ @NonNull ComponentInfo info, @NonNull ParsedMainComponent component) {
+ assignFieldsPackageItemInfoParsedComponent(info, component);
+ info.descriptionRes = component.getDescriptionRes();
+ info.directBootAware = component.isDirectBootAware();
+ info.enabled = component.isEnabled();
+ info.splitName = component.getSplitName();
+ info.attributionTags = component.getAttributionTags();
+ info.nonLocalizedLabel = component.getNonLocalizedLabel();
+ info.icon = component.getIcon();
+ }
+
+ private static void assignFieldsPackageItemInfoParsedComponent(
+ @NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component) {
+ packageItemInfo.nonLocalizedLabel = ComponentParseUtils.getNonLocalizedLabel(component);
+ packageItemInfo.icon = ComponentParseUtils.getIcon(component);
+ packageItemInfo.banner = component.getBanner();
+ packageItemInfo.labelRes = component.getLabelRes();
+ packageItemInfo.logo = component.getLogo();
+ packageItemInfo.name = component.getName();
+ packageItemInfo.packageName = component.getPackageName();
+ }
+
+ private static void initForUser(ApplicationInfo output, AndroidPackage input,
+ @UserIdInt int userId) {
+ PackageImpl pkg = ((PackageImpl) input);
+ String packageName = input.getPackageName();
+ output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid()));
+
+ // For performance reasons, all these paths are built as strings
+ final String credentialDir = pkg.getBaseAppDataCredentialProtectedDirForSystemUser();
+ final String deviceDir = pkg.getBaseAppDataDeviceProtectedDirForSystemUser();
+ if (credentialDir != null && deviceDir != null) {
+ if (userId == UserHandle.USER_SYSTEM) {
+ output.credentialProtectedDataDir = credentialDir + packageName;
+ output.deviceProtectedDataDir = deviceDir + packageName;
+ } else {
+ // Convert /data/user/0/ -> /data/user/1/com.example.app
+ String userIdString = String.valueOf(userId);
+ int credentialLength = credentialDir.length();
+ output.credentialProtectedDataDir = new StringBuilder(credentialDir)
+ .replace(credentialLength - 2, credentialLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ int deviceLength = deviceDir.length();
+ output.deviceProtectedDataDir = new StringBuilder(deviceDir)
+ .replace(deviceLength - 2, deviceLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ }
+ }
+
+ if (input.isDefaultToDeviceProtectedStorage()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ output.dataDir = output.deviceProtectedDataDir;
+ } else {
+ output.dataDir = output.credentialProtectedDataDir;
+ }
+ }
+
+ // This duplicates the ApplicationInfo variant because it uses field assignment and the classes
+ // don't inherit from each other, unfortunately. Consolidating logic would introduce overhead.
+ private static void initForUser(InstrumentationInfo output, AndroidPackage input,
+ @UserIdInt int userId) {
+ PackageImpl pkg = ((PackageImpl) input);
+ String packageName = input.getPackageName();
+
+ // For performance reasons, all these paths are built as strings
+ final String credentialDir = pkg.getBaseAppDataCredentialProtectedDirForSystemUser();
+ final String deviceDir = pkg.getBaseAppDataDeviceProtectedDirForSystemUser();
+ if (credentialDir != null && deviceDir != null) {
+ if (userId == UserHandle.USER_SYSTEM) {
+ output.credentialProtectedDataDir = credentialDir + packageName;
+ output.deviceProtectedDataDir = deviceDir + packageName;
+ } else {
+ // Convert /data/user/0/ -> /data/user/1/com.example.app
+ String userIdString = String.valueOf(userId);
+ int credentialLength = credentialDir.length();
+ output.credentialProtectedDataDir = new StringBuilder(credentialDir)
+ .replace(credentialLength - 2, credentialLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ int deviceLength = deviceDir.length();
+ output.deviceProtectedDataDir = new StringBuilder(deviceDir)
+ .replace(deviceLength - 2, deviceLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ }
+ }
+
+ if (input.isDefaultToDeviceProtectedStorage()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ output.dataDir = output.deviceProtectedDataDir;
+ } else {
+ output.dataDir = output.credentialProtectedDataDir;
+ }
+ }
+
+ /**
+ * Test if the given component is considered system, enabled and a match for the given
+ * flags.
+ *
+ * <p>
+ * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and {@link
+ * PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
+ * </p>
+ */
+ private static boolean isMatch(AndroidPackage pkg,
+ boolean isComponentDirectBootAware, long flags) {
+ final boolean isSystem = ((AndroidPackageHidden) pkg).isSystem();
+ if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+ if (!isSystem) {
+ return reportIfDebug(false, flags);
+ }
+ }
+
+ final boolean matchesUnaware = ((flags & PackageManager.MATCH_DIRECT_BOOT_UNAWARE) != 0)
+ && !isComponentDirectBootAware;
+ final boolean matchesAware = ((flags & PackageManager.MATCH_DIRECT_BOOT_AWARE) != 0)
+ && isComponentDirectBootAware;
+ return reportIfDebug(matchesUnaware || matchesAware, flags);
+ }
+
+ private static boolean reportIfDebug(boolean result, long flags) {
+ if (DEBUG && !result) {
+ Slog.i(TAG, "No match!; flags: "
+ + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
+ + Debug.getCaller());
+ }
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/core/java/com/android/internal/pm/parsing/PackageParser2.java
similarity index 67%
rename from services/core/java/com/android/server/pm/parsing/PackageParser2.java
rename to core/java/com/android/internal/pm/parsing/PackageParser2.java
index b6a08a5..2c54672 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/core/java/com/android/internal/pm/parsing/PackageParser2.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.server.pm.parsing;
+package com.android.internal.pm.parsing;
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.content.Context;
+import android.app.Application;
import android.content.pm.ApplicationInfo;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseInput;
@@ -28,26 +28,21 @@
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.TypedArray;
import android.os.Build;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.permission.PermissionManager;
import android.util.DisplayMetrics;
import android.util.Slog;
-import com.android.internal.compat.IPlatformCompat;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.pm.pkg.parsing.ParsingUtils;
import com.android.internal.util.ArrayUtils;
-import com.android.server.SystemConfig;
-import com.android.server.pm.PackageManagerException;
-import com.android.server.pm.PackageManagerService;
import java.io.File;
+import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
/**
* The v2 of package parsing for use when parsing is initiated in the server and must
@@ -59,50 +54,6 @@
*/
public class PackageParser2 implements AutoCloseable {
- /**
- * For parsing inside the system server but outside of {@link PackageManagerService}.
- * Generally used for parsing information in an APK that hasn't been installed yet.
- *
- * This must be called inside the system process as it relies on {@link ServiceManager}.
- */
- @NonNull
- public static PackageParser2 forParsingFileWithDefaults() {
- IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
- return new PackageParser2(null /* separateProcesses */, null /* displayMetrics */,
- null /* cacheDir */, new Callback() {
- @Override
- public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
- try {
- return platformCompat.isChangeEnabled(changeId, appInfo);
- } catch (Exception e) {
- // This shouldn't happen, but assume enforcement if it does
- Slog.wtf(TAG, "IPlatformCompat query failed", e);
- return true;
- }
- }
-
- @Override
- public boolean hasFeature(String feature) {
- // Assume the device doesn't support anything. This will affect permission parsing
- // and will force <uses-permission/> declarations to include all requiredNotFeature
- // permissions and exclude all requiredFeature permissions. This mirrors the old
- // behavior.
- return false;
- }
-
- @Override
- public Set<String> getHiddenApiWhitelistedApps() {
- return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
- }
-
- @Override
- public Set<String> getInstallConstraintsAllowlist() {
- return SystemConfig.getInstance().getInstallConstraintsAllowlist();
- }
- });
- }
-
private static final String TAG = ParsingUtils.TAG;
private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
@@ -118,25 +69,34 @@
private final ThreadLocal<ParseTypeImpl> mSharedResult;
@Nullable
- protected PackageCacher mCacher;
+ protected IPackageCacher mCacher;
- private final ParsingPackageUtils parsingUtils;
+ private final ParsingPackageUtils mParsingUtils;
public PackageParser2(String[] separateProcesses, DisplayMetrics displayMetrics,
- @Nullable File cacheDir, @NonNull Callback callback) {
+ @Nullable IPackageCacher cacher, @NonNull Callback callback) {
if (displayMetrics == null) {
displayMetrics = new DisplayMetrics();
displayMetrics.setToDefaults();
}
- PermissionManager permissionManager = ActivityThread.currentApplication()
- .getSystemService(PermissionManager.class);
- List<PermissionManager.SplitPermissionInfo> splitPermissions = permissionManager
- .getSplitPermissions();
+ List<PermissionManager.SplitPermissionInfo> splitPermissions = null;
- mCacher = cacheDir == null ? null : new PackageCacher(cacheDir);
+ final Application application = ActivityThread.currentApplication();
+ if (application != null) {
+ final PermissionManager permissionManager =
+ application.getSystemService(PermissionManager.class);
+ if (permissionManager != null) {
+ splitPermissions = permissionManager.getSplitPermissions();
+ }
+ }
+ if (splitPermissions == null) {
+ splitPermissions = new ArrayList<>();
+ }
- parsingUtils = new ParsingPackageUtils(separateProcesses, displayMetrics, splitPermissions,
+ mCacher = cacher;
+
+ mParsingUtils = new ParsingPackageUtils(separateProcesses, displayMetrics, splitPermissions,
callback);
ParseInput.Callback enforcementCallback = (changeId, packageName, targetSdkVersion) -> {
@@ -155,7 +115,7 @@
*/
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
- throws PackageManagerException {
+ throws PackageParserException {
var files = packageFile.listFiles();
// Apk directory is directly nested under the current directory
if (ArrayUtils.size(files) == 1 && files[0].isDirectory()) {
@@ -171,9 +131,9 @@
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
ParseInput input = mSharedResult.get().reset();
- ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
+ ParseResult<ParsingPackage> result = mParsingUtils.parsePackage(input, packageFile, flags);
if (result.isError()) {
- throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+ throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
@@ -201,12 +161,12 @@
*/
@AnyThread
public ParsedPackage parsePackageFromPackageLite(PackageLite packageLite, int flags)
- throws PackageManagerException {
+ throws PackageParserException {
ParseInput input = mSharedResult.get().reset();
- ParseResult<ParsingPackage> result = parsingUtils.parsePackageFromPackageLite(input,
+ ParseResult<ParsingPackage> result = mParsingUtils.parsePackageFromPackageLite(input,
packageLite, flags);
if (result.isError()) {
- throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+ throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
return result.getResult().hideAsParsed();
@@ -226,7 +186,7 @@
mSharedAppInfo.remove();
}
- public static abstract class Callback implements ParsingPackageUtils.Callback {
+ public abstract static class Callback implements ParsingPackageUtils.Callback {
@Override
public final ParsingPackage startParsingPackage(@NonNull String packageName,
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 4474d4ca..70505a4 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -139,10 +139,13 @@
return queue->syncNextTransaction(
[globalCallbackRef](SurfaceComposerClient::Transaction* t) {
JNIEnv* env = getenv(globalCallbackRef->vm());
+ ScopedLocalRef<jobject>
+ transactionObject(env,
+ env->NewObject(gTransactionClassInfo.clazz,
+ gTransactionClassInfo.ctor,
+ reinterpret_cast<jlong>(t)));
env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept,
- env->NewObject(gTransactionClassInfo.clazz,
- gTransactionClassInfo.ctor,
- reinterpret_cast<jlong>(t)));
+ transactionObject.get());
},
acquireSingleBuffer);
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 9c883d1..56ea52d 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -225,6 +225,19 @@
return translateNativeSensorToJavaSensor(env, sensor, *sensorList[index]) != NULL;
}
+static jboolean nativeGetDefaultDeviceSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager,
+ jobject sensor, jint index) {
+ SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
+
+ Vector<Sensor> sensorList;
+ ssize_t count = mgr->getDefaultDeviceSensorList(sensorList);
+ if (ssize_t(index) >= count) {
+ return false;
+ }
+
+ return translateNativeSensorToJavaSensor(env, sensor, sensorList[index]) != NULL;
+}
+
static void
nativeGetDynamicSensors(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensorList) {
@@ -539,6 +552,9 @@
{"nativeGetSensorAtIndex", "(JLandroid/hardware/Sensor;I)Z",
(void *)nativeGetSensorAtIndex},
+ {"nativeGetDefaultDeviceSensorAtIndex", "(JLandroid/hardware/Sensor;I)Z",
+ (void *)nativeGetDefaultDeviceSensorAtIndex},
+
{"nativeGetDynamicSensors", "(JLjava/util/List;)V", (void *)nativeGetDynamicSensors},
{"nativeGetRuntimeSensors", "(JILjava/util/List;)V", (void *)nativeGetRuntimeSensors},
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index fef8ad7..f007cc5 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -41,6 +41,7 @@
jmethodID dispatchHotplugConnectionError;
jmethodID dispatchModeChanged;
jmethodID dispatchFrameRateOverrides;
+ jmethodID dispatchHdcpLevelsChanged;
struct {
jclass clazz;
@@ -96,6 +97,8 @@
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
+ void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int connectedLevel,
+ int maxLevel) override;
};
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -294,6 +297,22 @@
mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
+void NativeDisplayEventReceiver::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId,
+ int connectedLevel, int maxLevel) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking hdcp levels changed handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchHdcpLevelsChanged,
+ displayId.value, connectedLevel, maxLevel);
+ ALOGV("receiver %p ~ Returned from hdcp levels changed handler.", this);
+ }
+
+ mMessageQueue->raiseAndClearException(env, "dispatchHdcpLevelsChanged");
+}
+
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak,
jobject messageQueueObj, jint vsyncSource, jint eventRegistration,
jlong layerHandle) {
@@ -385,6 +404,9 @@
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
"dispatchFrameRateOverrides",
"(JJ[Landroid/view/DisplayEventReceiver$FrameRateOverride;)V");
+ gDisplayEventReceiverClassInfo.dispatchHdcpLevelsChanged =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHdcpLevelsChanged",
+ "(JII)V");
jclass frameRateOverrideClazz =
FindClassOrDie(env, "android/view/DisplayEventReceiver$FrameRateOverride");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7c5885a..7e325a5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2187,6 +2187,7 @@
}
if (multiuser_get_app_id(uid) == AID_NETWORK_STACK) {
+ capabilities |= (1LL << CAP_WAKE_ALARM);
capabilities |= (1LL << CAP_NET_ADMIN);
capabilities |= (1LL << CAP_NET_BROADCAST);
capabilities |= (1LL << CAP_NET_BIND_SERVICE);
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 052e2f2..d3f3af7 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -610,6 +610,9 @@
// ringer mode.
optional SettingProto mode_ringer = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // This is an optional feature where ringer mode affects alarm stream as well
+ optional SettingProto mute_alarm_stream_with_ringer_mode = 160 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
reserved 147; // Used to be apply_ramping_ringer
message MultiSim {
@@ -1086,5 +1089,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 160;
+ // Next tag = 161;
}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index c5889ba..75cfba0 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -429,6 +429,7 @@
optional string base_dir = 1;
optional string res_dir = 2;
optional string data_dir = 3;
+ optional int32 targetSdkVersion = 4;
}
optional AppInfo appinfo = 8;
optional ProcessRecordProto app = 9;
diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto
index 72d5303..53155d2 100644
--- a/core/proto/android/view/displaycutout.proto
+++ b/core/proto/android/view/displaycutout.proto
@@ -32,4 +32,5 @@
optional .android.graphics.RectProto bound_right = 5;
optional .android.graphics.RectProto bound_bottom = 6;
optional .android.graphics.RectProto waterfall_insets = 7;
+ repeated int32 side_overrides = 8;
}
diff --git a/core/proto/android/view/insetssource.proto b/core/proto/android/view/insetssource.proto
index e6c6d59..118dfc8 100644
--- a/core/proto/android/view/insetssource.proto
+++ b/core/proto/android/view/insetssource.proto
@@ -26,7 +26,7 @@
* Represents a {@link android.view.InsetsSource} object.
*/
message InsetsSourceProto {
- optional string type = 1;
+ optional string type = 1 [deprecated=true];
optional .android.graphics.RectProto frame = 2;
optional .android.graphics.RectProto visible_frame = 3;
optional bool visible = 4;
diff --git a/core/proto/android/view/insetssourceconsumer.proto b/core/proto/android/view/insetssourceconsumer.proto
index a01ad8e..882163f 100644
--- a/core/proto/android/view/insetssourceconsumer.proto
+++ b/core/proto/android/view/insetssourceconsumer.proto
@@ -27,11 +27,12 @@
* Represents a {@link android.view.InsetsSourceConsumer} object.
*/
message InsetsSourceConsumerProto {
- optional string internal_insets_type = 1;
+ optional string internal_insets_type = 1 [deprecated=true];
optional bool has_window_focus = 2;
optional bool is_requested_visible = 3;
optional InsetsSourceControlProto source_control = 4;
optional .android.graphics.RectProto pending_frame = 5;
optional .android.graphics.RectProto pending_visible_frame = 6;
optional int32 animation_state = 7;
+ optional int32 type_number = 8;
}
\ No newline at end of file
diff --git a/core/proto/android/view/insetssourcecontrol.proto b/core/proto/android/view/insetssourcecontrol.proto
index 3ac3cbf..afab57f 100644
--- a/core/proto/android/view/insetssourcecontrol.proto
+++ b/core/proto/android/view/insetssourcecontrol.proto
@@ -27,7 +27,8 @@
* Represents a {@link android.view.InsetsSourceControl} object.
*/
message InsetsSourceControlProto {
- optional string type = 1;
+ optional string type = 1 [deprecated=true];
optional .android.graphics.PointProto position = 2;
optional SurfaceControlProto leash = 3;
+ optional int32 type_number = 4;
}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c6209dd..232a36f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -578,6 +578,7 @@
<protected-broadcast android:name="com.android.settings.network.SWITCH_TO_SUBSCRIPTION" />
<protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" />
+ <protected-broadcast android:name="android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED" />
<protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
<protected-broadcast android:name="NotificationHistoryDatabase.CLEANUP" />
<protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 8e89075..8281462 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Naweek"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Geleentheid"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slaap"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Bestuur deur <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aan"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Af"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> demp sekere klanke"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Toets"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeenskaplik"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b38257c..ff7dee8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"የሳምንት እረፍት ቀናት"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ክስተት"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"መተኛት"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"በ<xliff:g id="APP_NAME">%1$s</xliff:g> የሚተዳደር"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"በርቷል"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ጠፍቷል"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> አንዳንድ ድምጾችን እየዘጋ ነው"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ሥራ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ሙከራ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"የጋራ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index a77fc65..70f0c93 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1910,8 +1910,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"نهاية الأسبوع"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"حدث"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"النوم"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"تحت إدارة \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"مفعَّل"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"غير مفعَّل"</string>
<string name="muted_by" msgid="91464083490094950">"يعمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> على كتم بعض الأصوات."</string>
@@ -2344,9 +2343,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"يسمح هذا الإذن للتطبيق المصاحب ببدء الخدمات التي تعمل في المقدّمة من الخلفية."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"الميكروفون متاح."</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"تم حظر الميكروفون."</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"يتعذّر إجراء نسخ مطابق لمحتوى جهازك إلى الشاشة"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"يتعذّر البثّ على الشاشة"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"يُرجى استخدام كابل آخر وإعادة المحاولة."</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"جهازك ساخن للغاية ولا يمكنه إجراء نسخ مطابق للمحتوى إلى الشاشة إلى أن تنخفض حرارته."</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"جهازك ساخن للغاية ولا يمكنه البث على الشاشة إلى أن تنخفض حرارته."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ميزة Dual Screen مفعّلة"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"يستخدم \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" كلتا الشاشتين لعرض المحتوى."</string>
@@ -2370,4 +2369,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ملف العمل 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ملف شخصي تجريبي"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ملف شخصي مشترك"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f85f61c..ddc2367 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1177,7 +1177,7 @@
<string name="yes" msgid="9069828999585032361">"ঠিক আছে"</string>
<string name="no" msgid="5122037903299899715">"বাতিল কৰক"</string>
<string name="dialog_alert_title" msgid="651856561974090712">"মনোযোগ দিব"</string>
- <string name="loading" msgid="3138021523725055037">"ল\'ড কৰি থকা হৈছে…"</string>
+ <string name="loading" msgid="3138021523725055037">"ল’ড হৈ আছে…"</string>
<string name="capital_on" msgid="2770685323900821829">"অন কৰক"</string>
<string name="capital_off" msgid="7443704171014626777">"অফ কৰক"</string>
<string name="checked" msgid="9179896827054513119">"টিক চিহ্ন দিয়া হৈছে"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"সপ্তাহ অন্ত"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"কার্যক্ৰম"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"নিদ্ৰাৰত"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ পৰিচালনা কৰা"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"অন আছে"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"অফ আছে"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>এ কিছুমান ধ্বনি মিউট কৰি আছে"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"কৰ্মস্থান ৩"</string>
<string name="profile_label_test" msgid="9168641926186071947">"পৰীক্ষা"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"শ্বেয়াৰ কৰা"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index ed1e340..794e26a 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Həftə sonu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tədbir"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Yuxu vaxtı"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> idarə edir"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktiv"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Deaktiv"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bəzi səsləri səssiz rejimə salır"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Kommunal"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ea15cee..8df8b87 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvuke"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 8da0d1a..13801d0 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Выхадныя"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Падзея"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Рэжым сну"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Пад кіраваннем праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Уключана"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Выключана"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> выключае некаторыя гукі"</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Працоўны 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тэставы"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Супольны"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index e97d9dc..e0e9b09 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Събота и неделя"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Събитие"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Време за сън"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Управлява се от <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Вкл."</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Изкл."</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> заглушава някои звуци"</string>
@@ -2340,9 +2339,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Разрешава на дадено придружаващо приложение да стартира услуги на преден план, докато се изпълнява на заден план."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофонът е налице"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофонът е блокиран"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не може да се копира огледално на дисплея"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не може да се дублира на дисплея"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Използвайте друг кабел и опитайте отново"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Устройството ви е твърде топло и няма да може да дублира на екрана, преди да се охлади"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Устройството ви е твърде топло. Дублирането ще е възможно, след като се охлади"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Функцията Dual Screen е включена"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> използва и двата екрана, за да показва съдържание"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Служебни 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тестване"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Общи"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index fa2c283..cf8c64d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"সপ্তাহান্ত"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ইভেন্ট"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ঘুমানোর সময়"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ম্যানেজ করে"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"চালু আছে"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"বন্ধ আছে"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> কিছু সাউন্ডকে মিউট করে দিচ্ছে"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"৩য় অফিস"</string>
<string name="profile_label_test" msgid="9168641926186071947">"পরীক্ষা"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"কমিউনাল"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index cd941ce..ef941e19f 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1902,13 +1902,12 @@
<string name="zen_mode_rule_name_combination" msgid="7174598364351313725">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="8009920446193610996">"Suzi"</string>
<string name="zen_mode_feature_name" msgid="3785547207263754500">"Ne ometaj"</string>
- <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Neaktivnost"</string>
+ <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Vrijeme mirovanja"</string>
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Radni dan uvečer"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"3. poslovno"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testno"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Opće"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fbdcf56..a4b01e8 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cap de setmana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esdeveniment"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Mentre dormo"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionat per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activat"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivat"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> està silenciant alguns sons"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Treball 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Prova"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartit"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 156f18d..e518de4 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Událost"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánek"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Spravováno aplikací <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Zapnuto"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuto"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypíná určité zvuky"</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Práce 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Komunální"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9f89293..7119131 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -367,11 +367,11 @@
<string name="permlab_receiveMms" msgid="4000650116674380275">"modtage tekstbeskeder (mms)"</string>
<string name="permdesc_receiveMms" msgid="958102423732219710">"Tillader, at appen kan modtage og behandle mms-beskeder. Det betyder, at appen kan overvåge eller slette de beskeder, der sendes til din enhed, uden at vise dem til dig."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Videresend Cell Broadcast-meddelelser"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Tillader, at appen bindes til Cell Broadcast-modulet, så Cell Broadcast-meddelelser kan videresendes, når de modtages. I nogle områder sendes der Cell Broadcast-underretninger for at advare dig om nødsituationer. Ondsindede apps kan forstyrre effektiviteten eller driften af din enhed, når den modtager en Cell Broadcast-meddelelse om en nødsituation."</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Tillader, at appen bindes til Cell Broadcast-modulet, så Cell Broadcast-meddelelser kan videresendes, når de modtages. I regioner sendes der Cell Broadcast-underretninger for at advare dig om nødsituationer. Ondsindede apps kan forstyrre effektiviteten eller driften af din enhed, når den modtager en Cell Broadcast-meddelelse om en nødsituation."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Administrere igangværende opkald"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Giver appen tilladelse til at se oplysninger om igangværende opkald på din enhed og styre disse opkald."</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"læse Cell Broadcast-meddelelser"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Tillader, at appen læser Cell Broadcast-underretninger, der modtages af din enhed. I nogle områder sendes der Cell Broadcast-underretninger for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af din enhed, når der modtages en Cell Broadcast-meddelelse om en nødsituation."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Tillader, at appen læser Cell Broadcast-underretninger, der modtages af din enhed. I regioner sendes der Cell Broadcast-underretninger for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af din enhed, når der modtages en Cell Broadcast-meddelelse om en nødsituation."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"læse feeds, jeg abonnerer på"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Tillader, at appen kan hente oplysninger om de feeds, der synkroniseres."</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"Send og se sms-beskeder"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Begivenhed"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administreres af <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Til"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Fra"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår nogle lyde fra"</string>
@@ -1951,14 +1950,14 @@
<string name="user_creation_adding" msgid="7305185499667958364">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en nye bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="supervised_user_creation_label" msgid="6884904353827427515">"Tilføj en administreret bruger"</string>
<string name="language_selection_title" msgid="52674936078683285">"Tilføj et sprog"</string>
- <string name="country_selection_title" msgid="5221495687299014379">"Områdeindstilling"</string>
+ <string name="country_selection_title" msgid="5221495687299014379">"Regionpræferencer"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Angiv sprog"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Foreslået"</string>
<string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"Forslag"</string>
<string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"Foreslåede sprog"</string>
- <string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Foreslåede områder"</string>
+ <string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Foreslåede regioner"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle sprog"</string>
- <string name="region_picker_section_all" msgid="756441309928774155">"Alle områder"</string>
+ <string name="region_picker_section_all" msgid="756441309928774155">"Alle regioner"</string>
<string name="locale_search_menu" msgid="6258090710176422934">"Søg"</string>
<string name="app_suspended_title" msgid="888873445010322650">"Appen er ikke tilgængelig"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er ikke tilgængelig lige nu. Dette administreres af <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
@@ -1998,7 +1997,7 @@
<string name="profile_encrypted_title" msgid="9001208667521266472">"Nogle funktioner er begrænsede"</string>
<string name="profile_encrypted_detail" msgid="5279730442756849055">"Arbejdsprofilen er låst"</string>
<string name="profile_encrypted_message" msgid="1128512616293157802">"Tryk for at låse profilen op"</string>
- <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Tilsluttet <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
+ <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Forbundet <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
<string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Tryk for at se filer"</string>
<string name="pin_target" msgid="8036028973110156895">"Fastgør"</string>
<string name="pin_specific_target" msgid="7824671240625957415">"Fastgør <xliff:g id="LABEL">%1$s</xliff:g>"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Arbejde 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Fælles"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 182b974..c0e12c3 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wochenende"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Termin"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Schlafen"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Verwaltet von <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"An"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Aus"</string>
<string name="muted_by" msgid="91464083490094950">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string>
@@ -2340,8 +2339,8 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Ermöglicht einer Companion-App, Dienste im Vordergrund aus dem Hintergrund zu starten."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofon ist verfügbar"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon ist blockiert"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Kann nicht auf das Display gespiegelt werden"</string>
- <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Verwende ein anderes Kabel und versuch es noch einmal"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Bildschirm kann nicht gespiegelt werden"</string>
+ <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Versuch es mit einem anderen Kabel"</string>
<string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Dein Gerät ist zu heiß und kann den Bildschirm erst spiegeln, wenn es abgekühlt ist"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen ist aktiviert"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Geschäftlich 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeinsam genutzt"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d33a2f3..a392c0f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Σαββατοκύριακο"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Συμβάν"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ύπνος"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Διαχείριση από <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ενεργός"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Ανενεργός"</string>
<string name="muted_by" msgid="91464083490094950">"Το τρίτο μέρος <xliff:g id="THIRD_PARTY">%1$s</xliff:g> θέτει ορισμένους ήχους σε σίγαση"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Εργασία 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Δοκιμή"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Κοινόχρηστο"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a8d807e..7498488 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 2885e4d..fd76ce5 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ed4e953..1f2ccce 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c7e4252..d4fd01e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 8efab74..8a89ebd 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sleeping"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Managed by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> is muting some sounds"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 9cea763..f6117cd 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administradas por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activado"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Probar"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 3acb71f..406f879 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmiendo"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionado por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activado"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivado"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> silencia algunos sonidos"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Prueba"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Común"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 5a18823..afcc618 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Nädalavahetus"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sündmus"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Magamine"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Haldab <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Sees"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Väljas"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vaigistab teatud helid"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Töö 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Jagatud"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 625bb00..cf18eb9 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -558,7 +558,7 @@
<string name="permlab_changeTetherState" msgid="9079611809931863861">"aldatu telefono bidezko konektagarritasuna"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"Partekatutako Interneterako konexioaren egoera aldatzeko baimena ematen die aplikazioei."</string>
<string name="permlab_accessWifiState" msgid="5552488500317911052">"ikusi wifi-konexioak"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Wi-Fi sareei buruzko informazioa ikusteko baimena ematen die aplikazioei, adibidez, Wi-Fi konexioa aktibatuta dagoen eta konektatutako Wi-Fi gailuen izenak zein diren."</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Wi-Fi sareei buruzko informazioa ikusteko baimena ematen die aplikazioei, adibidez, wifi-konexioa aktibatuta dagoen eta konektatutako Wi-Fi gailuen izenak zein diren."</string>
<string name="permlab_changeWifiState" msgid="7947824109713181554">"konektatu wifira edo deskonektatu bertatik"</string>
<string name="permdesc_changeWifiState" msgid="7170350070554505384">"Wi-Fi sarbide-puntuetara konektatzeko edo haietatik deskonektatzeko baimena ematen die aplikazioei, baita Wi-Fi sareen gailu-konfigurazioari aldaketak egitekoa ere."</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"onartu Wi-Fi Multicast harrera"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Asteburua"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Gertaera"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Lo egiteko"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Kudeatzailea: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktibatuta"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desaktibatuta"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> soinu batzuk isilarazten ari da"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Lanekoa 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Probakoa"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Partekatua"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index be9c114..eb71a7d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -338,7 +338,7 @@
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"نوشتاری را که تایپ میکنید مشاهده کند"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"اطلاعات شخصی مانند شماره کارت اعتباری و گذرواژهها را لحاظ میکند."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"کنترل درشتنمایی نمایشگر"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"سطح و موقعیت بزرگنمایی نمایشگر را کنترل کنید."</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"موقعیت و سطح بزرگنمایی نمایشگر را کنترل کنید."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"اجرای اشارهها"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"میتوانید ضربه بزنید، انگشتتان را تند بکشید، انگشتانتان را به هم نزدیک یا از هم دور کنید و اشارههای دیگری اجرا کنید."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"اشارههای اثر انگشت"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"آخر هفته"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"رویداد"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"خوابیدن"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"تحتمدیریت <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"روشن"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"خاموش"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> درحال قطع کردن بعضی از صداهاست"</string>
@@ -2340,9 +2339,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"به برنامه همراه اجازه میدهد سرویسهای پیشنما را از پسزمینه راهاندازی کند."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"میکروفون دردسترس است"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"میکروفون مسدود شد"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"بازتاب دادن به نمایشگر ممکن نبود"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"قرینهسازی روی نمایشگر ممکن نبود"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"از کابل دیگری استفاده کنید و دوباره امتحان کنید"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"دستگاه بسیار گرم است و تا زمانیکه خنک نشود نمیتواند محتوا را در نمایشگر بازتاب دهد."</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"دستگاه بسیار گرم است و تا زمانیکه خنک نشود نمیتواند محتوا را روی نمایشگر قرینهسازی کند."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen روشن است"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> از هر دو نمایشگر برای نمایش محتوا استفاده میکند"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"کار ۳"</string>
<string name="profile_label_test" msgid="9168641926186071947">"آزمایش"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"عمومی"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d34817e..8359bf6 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Viikonloppuna"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tapahtuma"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Nukkuminen"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Ylläpitäjä: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Päällä"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Pois päältä"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mykistää joitakin ääniä"</string>
@@ -2342,7 +2341,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofoni on estetty"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Näytön peilaaminen ei onnistu"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Käytä eri johtoa ja yritä uudelleen"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Laite on liian kuuma eikä voi peilata näyttöön, kunnes se viilenee"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Laite on liian kuuma eikä voi peilata näyttöön, ennen kuin se viilenee"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Kaksoisnäyttö"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Kaksoisnäyttö on päällä"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> käyttää molempia näyttöjä sisällön näyttämiseen"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Työ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testi"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Jaettu"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index ba7fd52..8842ae8 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semaine"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 06b9ec8..d435c62 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Week-end"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Événement"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sommeil"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Géré par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activé"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Désactivé"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> coupe certains sons"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index f599631..703ed7c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmindo"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Xestionada por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivada"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando algúns sons"</string>
@@ -2342,7 +2341,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"O micrófono está bloqueado"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Non se pode proxectar contido na pantalla"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Cambia de cable e téntao de novo"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"O teu dispositivo está demasiado quente; mentres non arrefríe, non se poderá proxectar contido na pantalla"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"O dispositivo está moi quente; mentres non arrefríe, non se poderá proxectar contido na pantalla"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen está activada"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando ambas as pantallas para mostrar contido"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Traballo 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Proba"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5e12c7d..2e1f0e6 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"સપ્તાહાંત"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ઇવેન્ટ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"નિષ્ક્રિય"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> દ્વારા મેનેજ કરવામાં આવે છે"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ચાલુ છે"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"બંધ છે"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> અમુક અવાજોને મ્યૂટ કરે છે"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ઑફિસ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"પરીક્ષણ કરો"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"કૉમ્યુનલ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a6d70dd..336d998 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1226,7 +1226,7 @@
<string name="aerr_report" msgid="3095644466849299308">"फ़ीडबैक भेजें"</string>
<string name="aerr_close" msgid="3398336821267021852">"बंद करें"</string>
<string name="aerr_mute" msgid="2304972923480211376">"डिवाइस फिर से प्रारंभ होने तक म्यूट करें"</string>
- <string name="aerr_wait" msgid="3198677780474548217">"प्रतीक्षा करें"</string>
+ <string name="aerr_wait" msgid="3198677780474548217">"इंतज़ार करें"</string>
<string name="aerr_close_app" msgid="8318883106083050970">"ऐप बंद करें"</string>
<string name="anr_title" msgid="7290329487067300120"></string>
<string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> प्रतिसाद नहीं दे रहा है"</string>
@@ -1235,7 +1235,7 @@
<string name="anr_process" msgid="1664277165911816067">"<xliff:g id="PROCESS">%1$s</xliff:g> प्रक्रिया प्रतिसाद नहीं दे रही है"</string>
<string name="force_close" msgid="9035203496368973803">"ठीक है"</string>
<string name="report" msgid="2149194372340349521">"रिपोर्ट करें"</string>
- <string name="wait" msgid="7765985809494033348">"प्रतीक्षा करें"</string>
+ <string name="wait" msgid="7765985809494033348">"इंतज़ार करें"</string>
<string name="webpage_unresponsive" msgid="7850879412195273433">"पेज प्रतिसाद नहीं दे रहा है.\n\nक्या आप इसे बंद करना चाहते हैं?"</string>
<string name="launch_warning_title" msgid="6725456009564953595">"ऐप को दूसरे वेबलिंक पर लॉन्च किया गया"</string>
<string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> अभी चल रहा है."</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"सप्ताहांत"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"इवेंट"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"सोते समय"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"मैनेज करने वाला ऐप्लिकेशन: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"चालू है"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"बंद है"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> कुछ आवाज़ें म्यूट कर रहा है"</string>
@@ -2340,9 +2339,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"इससे साथी ऐप्लिकेशन को बैकग्राउंड में फ़ोरग्राउंड सेवाएं चलाने की अनुमति मिलती है."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"माइक्रोफ़ोन इस्तेमाल किया जा सकता है"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"माइक्रोफ़ोन को ब्लॉक किया गया है"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"डिसप्ले का कॉन्टेंट नहीं दिखाया जा सकता"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"कॉन्टेंट डिसप्ले नहीं किया जा सकता"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"कोई दूसरा केबल इस्तेमाल करके फिर से कोशिश करें"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"आपका डिवाइस बहुत गर्म है. इसलिए, इसके ठंडा होने तक दूसरे डिसप्ले पर इसकी स्क्रीन शेयर नहीं की जा सकती"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"आपका डिवाइस बहुत गर्म हो गया है. जब तक यह ठंडा नहीं हो जाता, तब तक दूसरे डिवाइस पर इसकी स्क्रीन डिसप्ले नहीं की जा सकती"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen की सुविधा चालू है"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g>, कॉन्टेंट दिखाने के लिए दोनों स्क्रीन का इस्तेमाल कर रहा है"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ऑफ़िस 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"टेस्ट"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"कम्यूनिटी"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e574b9a..ec5a77e 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Događaj"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spavanje"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Uključeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Isključeno"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> isključuje neke zvukove"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index a7e67f6..3bcd44b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hétvége"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Esemény"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Alvás"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Kezelő: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Bekapcsolva"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kikapcsolva"</string>
<string name="muted_by" msgid="91464083490094950">"A(z) <xliff:g id="THIRD_PARTY">%1$s</xliff:g> lenémít néhány hangot"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"3. munkahelyi"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teszt"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Közös"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 6e517fb..603d7c6 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Շաբաթ-կիրակի"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Միջոցառում"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Քնի ժամանակ"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Կառավարվում է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Միացված է"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Անջատված է"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ն անջատում է որոշ ձայներ"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Աշխատանքային 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Փորձնական"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Ընդհանուր"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f263f47..7886280 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Akhir pekan"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Dikelola oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktif"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Nonaktif"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> mematikan beberapa suara"</string>
@@ -2342,7 +2341,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon diblokir"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Tidak dapat mencerminkan ke layar"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Gunakan kabel lain dan coba lagi"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Perangkat terlalu panas dan tidak dapat mencerminkan ke layar sampai cukup dingin"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Perangkat terlalu panas dan tidak dapat mencerminkan ke layar sebelum suhunya cukup dingin"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen aktif"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> menggunakan kedua layar untuk menampilkan konten"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Pengujian"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 25608cf..ddf60c1 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helgi"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Viðburður"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Svefn"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Stýrt af <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Kveikt"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Slökkt"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> þaggar í einhverjum hljóðum"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Vinna 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Prófun"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Sameiginlegt"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a99fbe6..3877ac4 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fine settimana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Notte"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestione: app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"On"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Off"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> sta disattivando alcuni suoni"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Lavoro 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Condiviso"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 66250b0..eb2f3b2 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"סוף השבוע"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"אירוע"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"שינה"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"בניהול של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"מצב פעיל"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"מצב מושבת"</string>
<string name="muted_by" msgid="91464083490094950">"חלק מהצלילים מושתקים על ידי <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"פרופיל עבודה 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"בדיקה"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"שיתופי"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 8b87228..a2d40af 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -322,7 +322,7 @@
<string name="permgrouplab_camera" msgid="9090413408963547706">"カメラ"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"写真と動画の撮影"</string>
<string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"付近のデバイス"</string>
- <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"付近のデバイスの\\n検出と接続"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"付近のデバイスの検出と接続"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"通話履歴"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"通話履歴の読み取りと書き込み"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"電話"</string>
@@ -1872,7 +1872,7 @@
<string name="managed_profile_label_badge_2" msgid="5673187309555352550">"2 番目の仕事用<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="6882151970556391957">"3 番目の仕事用<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g> のクローン"</string>
- <string name="private_profile_label_badge" msgid="1712086003787839183">"個人用<xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="private_profile_label_badge" msgid="1712086003787839183">"プライベートの<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"オフライン再生を解除する前にPINの入力を求める"</string>
<string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"画面固定を解除する前にロック解除パターンの入力を求める"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"オフライン再生を解除する前にパスワードの入力を求める"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"予定"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠中"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> によって管理されています"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ON"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"OFF"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> により一部の音はミュートに設定"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"仕事用 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"テスト"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 839a817..dee1f1f 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"შაბათ-კვირა"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"მოვლენა"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ძილისას"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"მართავს <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ჩართული"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"გამორთული"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ზოგიერთ ხმას ადუმებს"</string>
@@ -2342,7 +2341,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"მიკროფონი დაბლოკილია"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ეკრანზე არეკვლა შეუძლებელია"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"გამოიყენეთ სხვა კაბელი და ცადეთ ხელახლა"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"თქვენი მოწყობილობა ძალიან თბილია და ვერ ასახავს ეკრანზე სანამ არ გაგრილდება"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"თქვენი მოწყობილობა ძალიან თბილია და ეკრანზე არეკვლას ვერ მოახერხებს, სანამ არ გაგრილდება"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"ორმაგი ეკრანი"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"ორმაგი ეკრანი ჩართულია"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> იყენებს ორივე ეკრანს შინაარსის საჩვენებლად"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"სამსახური 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"სატესტო"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"საერთო"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 45abbef..4cf61ac 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Демалыс күндері"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Іс-шара"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ұйқы режимі"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> басқарады."</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Қосулы"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Өшірулі"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> кейбір дыбыстарды өшіруде"</string>
@@ -2340,8 +2339,8 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Серік қолданбаға экрандық режимдегі қызметтерді фоннан іске қосуға рұқсат беріледі."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофон қолжетімді."</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон блокталған."</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Дисплейге көшірмені көрсету мүмкін емес"</string>
- <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Басқа кабельмен әрекетті қайталап көріңіз."</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Экран көшірмесін көрсету мүмкін емес"</string>
+ <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Басқа кабельмен көріңіз."</string>
<string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Құрылғыңыз тым қызып кетті, сондықтан ол суымайынша, дисплейге экран көшірмесін көрсете алмайды."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual Screen функциясы қосулы"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Жұмыс 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 3aec1f6..7973f2a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ចុងសប្ដាហ៍"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ព្រឹត្តិការណ៍"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"កំពុងដេក"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"គ្រប់គ្រងដោយ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"បើក"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"បិទ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> កំពុងបិទសំឡេងមួយចំនួន"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ការងារទី 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ការធ្វើតេស្ត"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ទូទៅ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 388fe20..7e5e5b5 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ವಾರಾಂತ್ಯ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ಈವೆಂಟ್"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ನಿದ್ರೆಯ ಸಮಯ"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗಿದೆ"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ಆನ್ ಆಗಿದೆ"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ಆಫ್ ಆಗಿದೆ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಧ್ವನಿ ಮ್ಯೂಟ್ ಮಾಡುತ್ತಿದ್ದಾರೆ"</string>
@@ -2340,7 +2339,7 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ಮುನ್ನೆಲೆ ಸೇವೆಗಳನ್ನು ಹಿನ್ನೆಲೆಯಿಂದ ಪ್ರಾರಂಭಿಸಲು ಕಂಪ್ಯಾನಿಯನ್ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"ಮೈಕ್ರೊಫೋನ್ ಲಭ್ಯವಿದೆ"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ಡಿಸ್ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ಡಿಸ್ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"ಬೇರೆ ಕೇಬಲ್ ಬಳಸಿ ಹಾಗೂ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"ನಿಮ್ಮ ಸಾಧನವು ತುಂಬಾ ಬಿಸಿಯಾಗಿದೆ ಮತ್ತು ಅದು ತಣ್ಣಗಾಗುವವರೆಗೆ ಡಿಸ್ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ಕೆಲಸ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ಪರೀಕ್ಷೆ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ಸಮುದಾಯ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index f002fd5..cfc7730 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"주말"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"캘린더 일정"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"수면 시간"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"관리자: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"사용"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"사용 중지"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(이)가 일부 소리를 음소거함"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"직장 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"테스트"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"공동"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 73161e4..dcdfc80 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Дем алыш"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Иш-чара"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Уйку режими"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> башкарат"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Күйүк"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Өчүк"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> айрым үндөрдү өчүрүүдө"</string>
@@ -2340,7 +2339,7 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Көмөкчү колдонмого активдүү кызматтарды фондо иштетүүгө уруксат берет."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофон жеткиликтүү"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон бөгөттөлгөн"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Экранга күзгүдөй чагылдыруу мүмкүн эмес"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Башка экранга чыгаруу мүмкүн эмес"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Башка кабелди колдонуп, кайра аракет кылыңыз"</string>
<string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Түзмөгүңүз өтө ысып кетти жана ал муздамайынча башка экранга чыгара албайт"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Кош экран"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Жумуш 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Сыноо"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index e687436..49266a4 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ທ້າຍອາທິດ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ການນັດໝາຍ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ການນອນ"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"ຈັດການໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ເປີດຢູ່"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ປິດຢູ່"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ປິດສຽງບາງຢ່າງໄວ້"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ວຽກ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ທົດສອບ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ສ່ວນກາງ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index b3c9f37..aefda2e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Savaitgalį"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Įvykis"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Miegas"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Tvarko „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Įjungti"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Išjungti"</string>
<string name="muted_by" msgid="91464083490094950">"„<xliff:g id="THIRD_PARTY">%1$s</xliff:g>“ nutildo kai kuriuos garsus"</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Darbas (3)"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Bandymas"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Bendruomenės"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a14e7c5..f17f02f 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Nedēļas nogalē"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Pasākums"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Gulēšana"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Pārvalda <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ieslēgta"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izslēgta"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izslēdz noteiktas skaņas"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Darbam (3.)"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testēšanai"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Kopīgs"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index e4ba9fa..dbcf11f 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Настан"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Спиење"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Управувано од <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Вклучено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Исклучено"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> исклучи некои звуци"</string>
@@ -2340,7 +2339,7 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Дозволува придружна апликација да започне услуги во преден план од заднината."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофонот е достапен"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофонот е блокиран"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не може да се отсликува за прикажување"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не може да се отслика екранот"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Користете друг кабел и обидете се повторно"</string>
<string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Вашиот уред е премногу топол и не може да се отсликува на екранот додека не се излади"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Работен профил 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Профил за тестирање"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Профил на заедницата"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 8aa2c9a..68c0749 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"വാരാന്ത്യം"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ഇവന്റ്"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ഉറക്കം"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യുന്നത്"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ഓണാണ്"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ഓഫാണ്"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ചില ശബ്ദങ്ങൾ മ്യൂട്ട് ചെയ്യുന്നു"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ഔദ്യോഗികം 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ടെസ്റ്റ്"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"കമ്മ്യൂണൽ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 27bcd80..97858ef2 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Амралтын өдөр"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Үйл явдал"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Унтлагын цаг"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g>-с удирддаг"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Асаалттай"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Унтраалттай"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> зарим дууны дууг хааж байна"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Ажил 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Туршилт"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Нийтийн"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 48d4fdb..cd553a4 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"आठवड्याच्या शेवटी"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"इव्हेंट"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"झोपताना"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> द्वारे व्यवस्थापित"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"सुरू आहे"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"बंद आहे"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्वनी म्यूट करत आहे"</string>
@@ -2342,7 +2341,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"मायक्रोफोन ब्लॉक केलेला आहे"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"डिस्प्लेवर मिरर करू शकत नाही"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"वेगळी केबल वापरून पुन्हा प्रयत्न करा"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"तुमचे डिव्हाइस खूप गरम आहे आणि ते थंड होईपर्यंत डिस्प्लेमध्ये मिरर करू शकत नाही"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"तुमचे डिव्हाइस खूप गरम आहे आणि ते थंड होईपर्यंत डिस्प्लेवर मिरर करू शकत नाही"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Dual screen सुरू आहे"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"आशय दाखवण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> दोन्ही डिस्प्ले वापरत आहे"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ऑफिस ३"</string>
<string name="profile_label_test" msgid="9168641926186071947">"चाचणी"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5254490..90ffb21 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hujung minggu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Acara"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Tidur"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Diurus oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Hidup"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Mati"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> meredamkan sesetengah bunyi"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Ujian"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 9e8a4a9..fa5efa6 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"စနေ၊ တနင်္ဂနွေ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"အစီအစဉ်"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"အိပ်နေချိန်"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> က စီမံသည်"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ဖွင့်"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ပိတ်"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> သည် အချို့အသံကို ပိတ်နေသည်"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"အလုပ် ၃"</string>
<string name="profile_label_test" msgid="9168641926186071947">"စမ်းသပ်မှု"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"အများသုံး"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 196f98d..f97e437 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Helg"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Aktivitet"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Administreres av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"På"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> slår av noen lyder"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Jobb 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Felles"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 1411463..99dcae0 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -978,7 +978,7 @@
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"फेरि प्रयास गर्नुहोस्"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"सबै सुविधाहरू र डेटाका लागि अनलक गर्नुहोस्"</string>
- <string name="faceunlock_multiple_failures" msgid="681991538434031708">"फेस अनलक प्रयोग गरी अनलक गर्ने प्रयास अत्याधिक धेरै भयो"</string>
+ <string name="faceunlock_multiple_failures" msgid="681991538434031708">"फेस अनलक प्रयोग गरी अनलक गर्ने प्रयास अत्यधिक धेरै भयो"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"SIM कार्ड हालिएको छैन"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"ट्याब्लेटमा SIM कार्ड हालिएको छैन।"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"तपाईंको Android TV डिभाइसमा SIM कार्ड हालिएको छैन।"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"शनिबार"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"कार्यक्रम"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"निदाएका बेला"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले व्यवस्थापन गरेको"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"अन छ"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"अफ छ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही ध्वनिहरू म्युट गर्दै छ"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"कार्य प्रोफाइल ३"</string>
<string name="profile_label_test" msgid="9168641926186071947">"परीक्षण"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c41dae7..f0d34a5 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Afspraken"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slapen"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Beheerd door <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aan"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Uit"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeenschappelijk"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 7a1b7f4..bfde6c9 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -277,7 +277,7 @@
<string name="notification_channel_account" msgid="6436294521740148173">"ଆକାଉଣ୍ଟର ସ୍ଥିତି"</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_updates" msgid="7907863984825495278">"ଅପଡେଟ କରନ୍ତୁ"</string>
<string name="notification_channel_network_status" msgid="2127687368725272809">"ନେଟୱର୍କ ସ୍ଥିତି"</string>
<string name="notification_channel_network_alerts" msgid="6312366315654526528">"ନେଟୱର୍କ ଅଲର୍ଟ"</string>
<string name="notification_channel_network_available" msgid="6083697929214165169">"ନେଟ୍ୱର୍କ ଉପଲବ୍ଧ ଅଛି"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ସପ୍ତାହାନ୍ତ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ଇଭେଣ୍ଟ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ଶୋଇବା"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ଚାଲୁ ଅଛି"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ବନ୍ଦ ଅଛି"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> କିଛି ସାଉଣ୍ଡକୁ ମ୍ୟୁଟ୍ କରୁଛି"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ୱାର୍କ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ଟେଷ୍ଟ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"କମ୍ୟୁନାଲ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index cccb3dd..d3901f7 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ਹਫ਼ਤੇ ਦਾ ਅੰਤਲਾ ਦਿਨ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ਇਵੈਂਟ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"ਸੌਣ ਵੇਲੇ"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ਚਾਲੂ ਹੈ"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ਬੰਦ ਹੈ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ਕੁਝ ਧੁਨੀਆਂ ਨੂੰ ਮਿਊਟ ਕਰ ਰਹੀ ਹੈ"</string>
@@ -1921,7 +1920,7 @@
<string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS ਬੇਨਤੀ ਨੂੰ ਵੀਡੀਓ ਕਾਲ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
<string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ਬੇਨਤੀ ਨੂੰ USSD ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
<string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ਨਵੀਂ SS ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string>
- <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ਫ਼ਿਸ਼ਿੰਗ ਸੰਬੰਧੀ ਸੁਚੇਤਨਾ"</string>
+ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ਫ਼ਿਸ਼ਿੰਗ ਸੰਬੰਧੀ ਅਲਰਟ"</string>
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"ਸੁਚੇਤਨਾਵਾਂ"</string>
<string name="notification_verified_content_description" msgid="6401483602782359391">"ਪੁਸ਼ਟੀਕਿਰਤ"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ਕੰਮ ਸੰਬੰਧੀ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ਜਾਂਚ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ਭਾਈਚਾਰਕ"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 5143e9e..e03679b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Wydarzenie"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sen"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Zarządzana przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Włączono"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Wyłączono"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> wycisza niektóre dźwięki"</string>
@@ -2344,7 +2343,7 @@
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon jest zablokowany"</string>
<string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Nie można utworzyć odbicia lustrzanego na wyświetlaczu"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Użyj innego kabla i spróbuj ponownie"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Urządzenie ma zbyt wysoką temperaturę i nie może utworzyć odbicia lustrzanego zawartości ekranu, dopóki się nie ochłodzi."</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Urządzenie jest zbyt ciepłe i nie może utworzyć odbicia lustrzanego zawartości ekranu, dopóki się nie ochłodzi."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Podwójny ekran"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Włączono podwójny ekran"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> korzysta z obu wyświetlaczy, aby pokazać treści"</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Służbowy 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testowy"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Wspólny"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index f7b6b0b..2888c5f 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerenciada pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5e6cc3e..7a3201f 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"A dormir"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerido por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está a desativar alguns sons."</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Comum"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index f7b6b0b..2888c5f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerenciada pelo app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index b5b411e..082fbb2 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Eveniment"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Somn"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gestionat de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activată"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Dezactivată"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Serviciu 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Comun"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 6948a1a..8a03a6b 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Выходные"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Мероприятие"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Время сна"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Под управлением приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Включено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Отключено"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> приглушает некоторые звуки."</string>
@@ -2342,9 +2341,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Сопутствующее приложение сможет запускать активные службы из фонового режима."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофон доступен."</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон заблокирован."</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не удается дублировать на экран"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Не удается дублировать экран"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Используйте другой кабель или повторите попытку."</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Ваше устройство слишком сильно нагрелось. Когда оно остынет, вы снова сможете передавать изображение на другой экран."</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Устройство слишком нагрелось и не может дублировать экран. Подождите, пока оно остынет."</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"Функция Dual Screen включена"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> использует оба экрана."</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Рабочий 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тестовый"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Совместный"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 7ed085d..89ee9f9 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"සති අන්තය"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"සිදුවීම"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"නිදා ගනිමින්"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් කළමනාකරණය කරයි"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ක්රියාත්මකයි"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ක්රියාවිරහිතයි"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> සමහර ශබ්ද නිහඬ කරමින්"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"කාර්යාලය 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"පරීක්ෂණය"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"වාර්ගික"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b6dd850..bbfefd1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Udalosť"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spánok"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Spravované aplikáciou <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Zapnuté"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Vypnuté"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vypína niektoré zvuky"</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testovací"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Spoločný"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index df7e614..e4e9a37 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Konec tedna"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Dogodek"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Spanje"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Upravlja <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Vklopljeno"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Izklopljeno"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> izklaplja nekatere zvoke"</string>
@@ -2342,7 +2341,7 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Spremljevalni aplikaciji dovoljuje, da storitve v ospredju zažene iz ozadja."</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"Mikrofon je na voljo"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"Mikrofon je blokiran"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ni mogoče zrcaliti zaslona"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Ni mogoče zrcaliti v zaslon"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Uporabite drug kabel in poskusite znova"</string>
<string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Naprava je pretopla in ne more zrcaliti v zaslon, dokler se ne ohladi"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Delo 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Preizkus"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Skupno"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index f84966b..288d8bf 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fundjava"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Ngjarje"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Në gjumë"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Menaxhohet nga <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Aktivizuar"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Çaktivizuar"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> po çaktivizon disa tinguj"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Puna 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"I përbashkët"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f37fe05..479a1db 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1907,8 +1907,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Догађај"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Спавање"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Управља: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Укључено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Искључено"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> искључује неке звуке"</string>
@@ -2367,4 +2366,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Посао 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тест"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Заједничко"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cbba6d44..a30aa0a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1614,7 +1614,7 @@
<string name="fingerprints" msgid="148690767172613723">"Fingeravtryck:"</string>
<string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256-fingeravtryck"</string>
<string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1-fingeravtryck:"</string>
- <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Visa alla"</string>
+ <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Se alla"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Välj aktivitet"</string>
<string name="share_action_provider_share_with" msgid="1904096863622941880">"Dela med"</string>
<string name="sending" msgid="206925243621664438">"Skickar ..."</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"I helgen"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Händelse"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"När jag sover"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Hanteras av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"På"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> stänger av vissa ljud"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Arbete 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ae9c3f8..2ff302d 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Wikendi"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tukio"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Kulala"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Inadhibitiwa na <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Imewashwa"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Imezimwa"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> inazima baadhi ya sauti"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Wa 3 wa Kazini"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Jaribio"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Unaoshirikiwa"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index cfa5c72..23402f7 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"வார இறுதி"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"நிகழ்வு"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"உறக்கத்தில்"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"நிர்வகிப்பது: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ஆன்"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ஆஃப்"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> சில ஒலிகளை முடக்குகிறது"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"பணி 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"பரிசோதனை"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"பொது"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 52d7539..30697b3 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"వారాంతం"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ఈవెంట్"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"నిద్రావస్థ"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ద్వారా మేనేజ్ చేయబడుతోంది"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ఆన్లో ఉంది"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ఆఫ్లో ఉంది"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ఆఫీస్ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"పరీక్ష"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"కమ్యూనల్"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 759b6e4..66ed053 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"สุดสัปดาห์"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"กิจกรรม"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"นอนหลับ"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"จัดการโดย <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"เปิด"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"ปิด"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> กำลังปิดเสียงบางรายการ"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"งาน 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ทดสอบ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ส่วนกลาง"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 650b3fe..c83daa3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Event"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Pag-sleep"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Pinapamahalaan ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Naka-on"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Naka-off"</string>
<string name="muted_by" msgid="91464083490094950">"Minu-mute ng <xliff:g id="THIRD_PARTY">%1$s</xliff:g> ang ilang tunog"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabaho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9e84fdd..c2c8ec6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Hafta sonu"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Etkinlik"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyku"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarafından yönetiliyor"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Açık"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kapalı"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> bazı sesleri kapatıyor"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Paylaşılan"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 2772eb4..00139a6 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1908,8 +1908,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"На вихідних"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Подія"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Під час сну"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Керує додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Увімкнено"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Вимкнено"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає деякі звуки"</string>
@@ -2368,4 +2367,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Робочий профіль 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тестовий профіль"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Спільний профіль"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 0324697..dc3ca47 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"ویک اینڈ"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"ایونٹ"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"سونا"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے زیر انتظام ہے"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"آن ہے"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"آف ہے"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کچھ آوازوں کو خاموش کر رہا ہے"</string>
@@ -2340,9 +2339,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"ساتھی ایپ کو پس منظر سے پیش منظر کی سروسز شروع کرنے کی اجازت دیتی ہے۔"</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"مائیکروفون دستیاب ہے"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"مائیکروفون مسدود ہے"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ڈسپلے پر دو طرفہ مطابقت پذیری ممکن نہیں ہے"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"ڈسپلے پر مرر نہیں ہو سکتا"</string>
<string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"مختلف کیبل استعمال کریں اور دوبارہ کوشش کریں"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"آپ کا آلہ بہت گرم ہے اور جب تک یہ ٹھنڈا نہیں ہو جاتا اس وقت تک ڈسپلے میں عکس دو طرفہ مطابقت پذیر نہیں ہو سکتا"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"آپ کا آلہ بہت گرم ہے اور جب تک یہ ٹھنڈا نہیں ہو جاتا اس وقت تک ڈسپلے پر مرر نہیں ہو سکتا"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"دوہری اسکرین"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"دوہری اسکرین آن ہے"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"مواد دکھانے کیلئے <xliff:g id="APP_NAME">%1$s</xliff:g> دونوں ڈسپلیز استعمال کر رہی ہے"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"تیسری دفتری پروفائل"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ٹیسٹ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"کمیونل"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 1a83288..23ef54c 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Dam olish kunlari"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Tadbir"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Uyquda"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> tomonidan boshqariladi"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Yoniq"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Oʻchiq"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ayrim tovushlarni ovozsiz qilgan"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Ish 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umumiy"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a6c64ee..22d9f06 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Cuối tuần"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Sự kiện"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ngủ"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Do <xliff:g id="APP_NAME">%1$s</xliff:g> quản lý"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Bật"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Tắt"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> đang tắt một số âm thanh"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Công việc 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Kiểm thử"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Dùng chung"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4b64daf..dec4705 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1882,8 +1882,8 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"确定"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"在省电模式下,系统会启用深色主题,并限制或关闭后台活动、某些视觉效果、特定功能和部分网络连接。"</string>
<string name="battery_saver_description" msgid="8518809702138617167">"在省电模式下,系统会启用深色主题,并限制或关闭后台活动、某些视觉效果、特定功能和部分网络连接。"</string>
- <string name="data_saver_description" msgid="4995164271550590517">"为了减少流量消耗,流量节省程序会阻止某些应用在后台收发数据。您当前使用的应用可以收发数据,但频率可能会降低。举例而言,这可能意味着图片只有在您点按之后才会显示。"</string>
- <string name="data_saver_enable_title" msgid="7080620065745260137">"要开启流量节省程序吗?"</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"为了减少流量消耗,省流模式会阻止某些应用在后台收发数据。您当前使用的应用可以收发数据,但频率可能会降低。举例而言,这可能意味着图片只有在您点按之后才会显示。"</string>
+ <string name="data_saver_enable_title" msgid="7080620065745260137">"要开启省流模式吗?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"开启"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{1 分钟(直到 {formattedTime})}other{# 分钟(直到 {formattedTime})}}"</string>
<string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{1 分钟(直到 {formattedTime})}other{# 分钟(直到 {formattedTime})}}"</string>
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"周末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活动"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由<xliff:g id="APP_NAME">%1$s</xliff:g>管理"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"已启用"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已停用"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正在将某些音效设为静音"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"测试"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 9610a38..769c274 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活動"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由<xliff:g id="APP_NAME">%1$s</xliff:g>管理"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"已開啟"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已關閉"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>正將某些音效設為靜音"</string>
@@ -2340,9 +2339,9 @@
<string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"允許隨附應用程式從背景啟動前景服務。"</string>
<string name="mic_access_on_toast" msgid="2666925317663845156">"可以使用麥克風"</string>
<string name="mic_access_off_toast" msgid="8111040892954242437">"已封鎖麥克風"</string>
- <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"無法將畫面鏡像投放至螢幕"</string>
- <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"請改用其他連接線,然後再試一次"</string>
- <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"裝置過熱,請等到降溫後再將畫面鏡像投射至螢幕"</string>
+ <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"無法鏡像投放"</string>
+ <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"請改用其他連接線再試"</string>
+ <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"裝置過熱,降溫後才可鏡像投放至螢幕"</string>
<string name="concurrent_display_notification_name" msgid="1526911253558311131">"雙螢幕"</string>
<string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"已開啟雙螢幕功能"</string>
<string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在使用雙螢幕顯示內容"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6164be5..2c8c046 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"活動"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"由「<xliff:g id="APP_NAME">%1$s</xliff:g>」管理"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"已啟用"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"已停用"</string>
<string name="muted_by" msgid="91464083490094950">"「<xliff:g id="THIRD_PARTY">%1$s</xliff:g>」正在關閉部分音效"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"通用"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f2a23bc..42f0b3f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1906,8 +1906,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Ngempelasonto"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Umcimbi"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Ulele"</string>
- <!-- no translation found for zen_mode_implicit_trigger_description (5714956693073007111) -->
- <skip />
+ <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Iphethwe yi-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Kuvuliwe"</string>
<string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Kuvaliwe"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ithulisa eminye imisindo"</string>
@@ -2366,4 +2365,5 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Umsebenzi 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Hlola"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Okomphakathi"</string>
+ <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 596cfe5..d1143c4 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2986,7 +2986,12 @@
depends on the number of isolated services that an application starts,
and how much memory those services save by preloading and sharing memory with
the app zygote. Therefore, it is recommended to measure memory usage under
- typical workloads to determine whether it makes sense to use this flag. -->
+ typical workloads to determine whether it makes sense to use this flag.
+
+ <p>There is a limit to the number of isolated services that can be spawned from
+ the Application Zygote; the absolute limit is 100, but due to potential
+ delays in service process cleanup, a much safer limit to use in practice is 50.
+ -->
<attr name="useAppZygote" format="boolean" />
<!-- If this is a foreground service, specify its category. -->
<attr name="foregroundServiceType" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d0de5f0..806be94 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2262,6 +2262,9 @@
<!-- The default min volume for the alarm stream -->
<integer name="config_audio_alarm_min_vol">1</integer>
+ <!-- Flag indicating if ringer mode affects alarm stream -->
+ <bool name="config_audio_ringer_mode_affects_alarm_stream">false</bool>
+
<!-- The default value for whether head tracking for
spatial audio is enabled for a newly connected audio device -->
<bool name="config_spatial_audio_head_tracking_enabled_default">false</bool>
@@ -4014,6 +4017,18 @@
by shrinking the display such that it does not overlap the cutout area. -->
<bool name="config_maskMainBuiltInDisplayCutout">false</bool>
+ <!-- This string array provide override side of each rotation of the given insets.
+ Array of "[rotation],[side]".
+ Undefined rotation will apply the default behavior.
+ When there are cutouts on multiple edges of the display, the override won't take any
+ effect. -->
+ <string-array name="config_mainBuiltInDisplayCutoutSideOverride" translatable="false">
+ <!-- Example:
+ <item>90,top</item>
+ <item>270,bottom</item>
+ -->
+ </string-array>
+
<!-- Ultrasound support for Mic/speaker path -->
<!-- Whether the default microphone audio source supports near-ultrasound frequencies
(range of 18 - 21 kHz). -->
@@ -6370,6 +6385,8 @@
</string>
<bool name="config_fillSecondaryBuiltInDisplayCutout">false</bool>
<bool name="config_maskSecondaryBuiltInDisplayCutout">false</bool>
+ <string-array name="config_secondaryBuiltInDisplayCutoutSideOverride" translatable="false">
+ </string-array>
<!-- An array contains unique ids of all built-in displays and the unique id of a display can be
obtained from {@link Display#getUniqueId}. This array should be set for multi-display
@@ -6415,6 +6432,11 @@
<item>@string/config_secondaryBuiltInDisplayCutoutRectApproximation</item>
</string-array>
+ <array name="config_displayCutoutSideOverrideArray" translatable="false">
+ <item>@array/config_mainBuiltInDisplayCutoutSideOverride</item>
+ <item>@array/config_secondaryBuiltInDisplayCutoutSideOverride</item>
+ </array>
+
<!-- The maskBuiltInDisplayCutout config for each display in a multi-display device. -->
<array name="config_maskBuiltInDisplayCutoutArray" translatable="false">
<item>@bool/config_maskMainBuiltInDisplayCutout</item>
@@ -6862,4 +6884,8 @@
<!-- Whether the media player is shown on the quick settings -->
<bool name="config_quickSettingsShowMediaPlayer">true</bool>
+
+ <!-- Defines suitability of the built-in speaker route.
+ Refer to {@link MediaRoute2Info} to see supported values. -->
+ <integer name="config_mediaRouter_builtInSpeakerSuitability">0</integer>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3894330..b0a4c16 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -288,6 +288,7 @@
<java-symbol type="integer" name="config_audio_ring_vol_default" />
<java-symbol type="integer" name="config_audio_ring_vol_steps" />
<java-symbol type="integer" name="config_audio_alarm_min_vol" />
+ <java-symbol type="bool" name="config_audio_ringer_mode_affects_alarm_stream" />
<java-symbol type="bool" name="config_spatial_audio_head_tracking_enabled_default" />
<java-symbol type="bool" name="config_avoidGfxAccel" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
@@ -4035,6 +4036,7 @@
<java-symbol type="string" name="global_action_logout" />
<java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
<java-symbol type="string" name="config_mainBuiltInDisplayCutoutRectApproximation" />
+ <java-symbol type="array" name="config_mainBuiltInDisplayCutoutSideOverride" />
<java-symbol type="drawable" name="messaging_user" />
<java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
<java-symbol type="drawable" name="ic_logout" />
@@ -5001,9 +5003,11 @@
<java-symbol type="string" name="config_secondaryBuiltInDisplayCutoutRectApproximation" />
<java-symbol type="bool" name="config_fillSecondaryBuiltInDisplayCutout" />
<java-symbol type="bool" name="config_maskSecondaryBuiltInDisplayCutout" />
+ <java-symbol type="array" name="config_secondaryBuiltInDisplayCutoutSideOverride" />
<java-symbol type="array" name="config_displayUniqueIdArray" />
<java-symbol type="array" name="config_displayCutoutPathArray" />
<java-symbol type="array" name="config_displayCutoutApproximationRectArray" />
+ <java-symbol type="array" name="config_displayCutoutSideOverrideArray" />
<java-symbol type="array" name="config_fillBuiltInDisplayCutoutArray" />
<java-symbol type="array" name="config_maskBuiltInDisplayCutoutArray" />
<java-symbol type="dimen" name="secondary_waterfall_display_left_edge_size" />
@@ -5306,4 +5310,7 @@
<java-symbol type="bool" name="config_viewBasedRotaryEncoderHapticsEnabled" />
<java-symbol type="bool" name="config_quickSettingsShowMediaPlayer" />
+
+ <!-- Android MediaRouter framework configs. -->
+ <java-symbol type="integer" name="config_mediaRouter_builtInSpeakerSuitability" />
</resources>
diff --git a/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java b/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java
index 2b4123a..73d7fe9 100644
--- a/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java
+++ b/core/tests/companiontests/src/android/companion/SystemDataTransportTest.java
@@ -16,6 +16,9 @@
package android.companion;
+import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_PING;
+import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PING;
+
import android.content.Context;
import android.os.SystemClock;
import android.test.InstrumentationTestCase;
@@ -36,16 +39,22 @@
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+/**
+ * Tests that CDM can intake incoming messages in the system data transport and output results.
+ *
+ * Build/Install/Run: atest CompanionTests:SystemDataTransportTest
+ */
public class SystemDataTransportTest extends InstrumentationTestCase {
private static final String TAG = "SystemDataTransportTest";
private static final int MESSAGE_INVALID = 0xF00DCAFE;
-
- private static final int MESSAGE_REQUEST_INVALID = 0x63636363; // ????
- private static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
-
+ private static final int MESSAGE_ONEWAY_INVALID = 0x43434343; // ++++
private static final int MESSAGE_RESPONSE_INVALID = 0x33333333; // !!!!
+ private static final int MESSAGE_REQUEST_INVALID = 0x63636363; // ????
+
private static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
private static final int MESSAGE_RESPONSE_FAILURE = 0x33706573; // !FAI
@@ -122,8 +131,6 @@
new Random().nextBytes(blob);
final byte[] input = generatePacket(MESSAGE_REQUEST_PING, /* sequence */ 1, blob);
- final byte[] expected = generatePacket(MESSAGE_RESPONSE_SUCCESS, /* sequence */ 1, blob);
- assertTransportBehavior(input, expected);
}
public void testMultiplePingPing() {
@@ -176,6 +183,43 @@
testPingHandRolled();
}
+ public void testInvalidOnewayMessages() throws InterruptedException {
+ // Add a callback
+ final CountDownLatch received = new CountDownLatch(1);
+ mCdm.addOnMessageReceivedListener(Runnable::run, MESSAGE_ONEWAY_INVALID,
+ (id, data) -> received.countDown());
+
+ final byte[] input = generatePacket(MESSAGE_ONEWAY_INVALID, /* sequence */ 1);
+ final ByteArrayInputStream in = new ByteArrayInputStream(input);
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ mCdm.attachSystemDataTransport(mAssociationId, in, out);
+
+ // Assert that a one-way message was ignored (does not trigger a callback)
+ assertFalse(received.await(5, TimeUnit.SECONDS));
+
+ // There should not be a response to one-way messages
+ assertEquals(0, out.toByteArray().length);
+ }
+
+
+ public void testOnewayMessages() throws InterruptedException {
+ // Add a callback
+ final CountDownLatch received = new CountDownLatch(1);
+ mCdm.addOnMessageReceivedListener(Runnable::run, MESSAGE_ONEWAY_PING,
+ (id, data) -> received.countDown());
+
+ final byte[] input = generatePacket(MESSAGE_ONEWAY_PING, /* sequence */ 1);
+ final ByteArrayInputStream in = new ByteArrayInputStream(input);
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ mCdm.attachSystemDataTransport(mAssociationId, in, out);
+
+ // Assert that a one-way message was received
+ assertTrue(received.await(1, TimeUnit.SECONDS));
+
+ // There should not be a response to one-way messages
+ assertEquals(0, out.toByteArray().length);
+ }
+
public static byte[] concat(byte[]... blobs) {
int length = 0;
for (byte[] blob : blobs) {
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 1577d9c1c1..5b0502d 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -1244,29 +1244,33 @@
}
@Test
- public void testBigPictureStyle_setExtras_pictureIconNull_noPictureIconKey() {
+ public void testBigPictureStyle_setExtras_pictureIconNull_pictureIconKeyNull() {
Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
bpStyle.bigPicture((Bitmap) null);
Bundle extras = new Bundle();
bpStyle.addExtras(extras);
- assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
+ assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
+ final Parcelable pictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
+ assertThat(pictureIcon).isNull();
}
@Test
- public void testBigPictureStyle_setExtras_pictureIconNull_noPictureKey() {
+ public void testBigPictureStyle_setExtras_pictureIconNull_pictureKeyNull() {
Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
bpStyle.bigPicture((Bitmap) null);
Bundle extras = new Bundle();
bpStyle.addExtras(extras);
- assertThat(extras.containsKey(EXTRA_PICTURE)).isFalse();
+ assertThat(extras.containsKey(EXTRA_PICTURE)).isTrue();
+ final Parcelable picture = extras.getParcelable(EXTRA_PICTURE);
+ assertThat(picture).isNull();
}
@Test
- public void testBigPictureStyle_setExtras_pictureIconTypeBitmap_noPictureIconKey() {
+ public void testBigPictureStyle_setExtras_pictureIconTypeBitmap_pictureIconKeyNull() {
Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
bpStyle.bigPicture(bitmap);
@@ -1274,11 +1278,13 @@
Bundle extras = new Bundle();
bpStyle.addExtras(extras);
- assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
+ assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
+ final Parcelable pictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
+ assertThat(pictureIcon).isNull();
}
@Test
- public void testBigPictureStyle_setExtras_pictureIconTypeIcon_noPictureKey() {
+ public void testBigPictureStyle_setExtras_pictureIconTypeIcon_pictureKeyNull() {
Icon icon = Icon.createWithResource(mContext, R.drawable.btn_plus);
Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
bpStyle.bigPicture(icon);
@@ -1286,7 +1292,9 @@
Bundle extras = new Bundle();
bpStyle.addExtras(extras);
- assertThat(extras.containsKey(EXTRA_PICTURE)).isFalse();
+ assertThat(extras.containsKey(EXTRA_PICTURE)).isTrue();
+ final Parcelable picture = extras.getParcelable(EXTRA_PICTURE);
+ assertThat(picture).isNull();
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
deleted file mode 100644
index 785a8a1..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
+++ /dev/null
@@ -1,73 +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.app.servertransaction;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-
-import android.app.Activity;
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link ActivityConfigurationChangeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:ActivityConfigurationChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class ActivityConfigurationChangeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private IBinder mToken;
- @Mock
- private Activity mActivity;
- // Can't mock final class.
- private final Configuration mConfiguration = new Configuration();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testGetContextToUpdate() {
- doReturn(mActivity).when(mHandler).getActivity(mToken);
-
- final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
- .obtain(mToken, mConfiguration);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mActivity, context);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
new file mode 100644
index 0000000..b5e8203
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.util.MergedConfiguration;
+import android.view.IWindow;
+import android.view.InsetsState;
+import android.window.ClientWindowFrames;
+import android.window.WindowContext;
+import android.window.WindowContextInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for subtypes of {@link ClientTransactionItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ClientTransactionItemTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ClientTransactionItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mActivityToken;
+ @Mock
+ private Activity mActivity;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mWindowClientToken;
+ @Mock
+ private WindowContext mWindowContext;
+ @Mock
+ private IWindow mWindow;
+
+ // Can't mock final class.
+ private Configuration mGlobalConfig;
+ private Configuration mConfiguration;
+ private ActivityThread.ActivityClientRecord mActivityClientRecord;
+ private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
+ private InsetsState mInsetsState;
+ private ClientWindowFrames mFrames;
+ private MergedConfiguration mMergedConfiguration;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mGlobalConfig = new Configuration();
+ mConfiguration = new Configuration();
+ mActivitiesToBeDestroyed = new ArrayMap<>();
+ mActivityClientRecord = new ActivityThread.ActivityClientRecord();
+ mInsetsState = new InsetsState();
+ mFrames = new ClientWindowFrames();
+ mMergedConfiguration = new MergedConfiguration(mGlobalConfig, mConfiguration);
+
+ doReturn(mActivity).when(mHandler).getActivity(mActivityToken);
+ doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
+ }
+
+ @Test
+ public void testActivityConfigurationChangeItem_getContextToUpdate() {
+ final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
+ .obtain(mActivityToken, mConfiguration);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mActivity, context);
+ }
+
+ @Test
+ public void testActivityRelaunchItem_getContextToUpdate() {
+ final ActivityRelaunchItem item = ActivityRelaunchItem
+ .obtain(mActivityToken, null /* pendingResults */, null /* pendingNewIntents */,
+ 0 /* configChange */, mMergedConfiguration, false /* preserveWindow */);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mActivity, context);
+ }
+
+ @Test
+ public void testConfigurationChangeItem_getContextToUpdate() {
+ final ConfigurationChangeItem item = ConfigurationChangeItem
+ .obtain(mConfiguration, DEVICE_ID_DEFAULT);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(ActivityThread.currentApplication(), context);
+ }
+
+ @Test
+ public void testDestroyActivityItem_preExecute() {
+ final DestroyActivityItem item = DestroyActivityItem
+ .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ item.preExecute(mHandler);
+
+ assertEquals(1, mActivitiesToBeDestroyed.size());
+ assertEquals(item, mActivitiesToBeDestroyed.get(mActivityToken));
+ }
+
+ @Test
+ public void testDestroyActivityItem_postExecute() {
+ final DestroyActivityItem item = DestroyActivityItem
+ .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ item.preExecute(mHandler);
+ item.postExecute(mHandler, mPendingActions);
+
+ assertTrue(mActivitiesToBeDestroyed.isEmpty());
+ }
+
+ @Test
+ public void testDestroyActivityItem_execute() {
+ final DestroyActivityItem item = DestroyActivityItem
+ .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ item.execute(mHandler, mActivityClientRecord, mPendingActions);
+
+ verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
+ eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
+ }
+
+ @Test
+ public void testLaunchActivityItem_getContextToUpdate() {
+ final LaunchActivityItem item = new TestUtils.LaunchActivityItemBuilder(
+ mActivityToken, new Intent(), new ActivityInfo())
+ .build();
+
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(ActivityThread.currentApplication(), context);
+ }
+
+ @Test
+ public void testMoveToDisplayItem_getContextToUpdate() {
+ final MoveToDisplayItem item = MoveToDisplayItem
+ .obtain(mActivityToken, DEFAULT_DISPLAY, mConfiguration);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mActivity, context);
+ }
+
+ @Test
+ public void testWindowContextInfoChangeItem_execute() {
+ final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
+ .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
+ item.execute(mHandler, mPendingActions);
+
+ verify(mHandler).handleWindowContextInfoChanged(mWindowClientToken,
+ new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
+ }
+
+ @Test
+ public void testWindowContextInfoChangeItem_getContextToUpdate() {
+ doReturn(mWindowContext).when(mHandler).getWindowContext(mWindowClientToken);
+
+ final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
+ .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mWindowContext, context);
+ }
+
+ @Test
+ public void testWindowContextWindowRemovalItem_execute() {
+ final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+ mWindowClientToken);
+ item.execute(mHandler, mPendingActions);
+
+ verify(mHandler).handleWindowContextWindowRemoval(mWindowClientToken);
+ }
+
+ @Test
+ public void testWindowStateResizeItem_execute() throws RemoteException {
+ final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
+ true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ item.execute(mHandler, mPendingActions);
+
+ verify(mWindow).resized(mFrames,
+ true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ }
+
+ @Test
+ public void testWindowStateResizeItem_getContextToUpdate() {
+ final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
+ true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(ActivityThread.currentApplication(), context);
+ }
+
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 930b1a4..95d5049 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -65,7 +65,7 @@
mHandler = getInstrumentation().getContext().getMainThreadHandler();
mController = spy(ClientTransactionListenerController.createInstanceForTesting(
mDisplayManager));
- doReturn(true).when(mController).isSyncWindowConfigUpdateFlagEnabled();
+ doReturn(true).when(mController).isBundleClientTransactionFlagEnabled();
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
deleted file mode 100644
index d9f5523..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
+++ /dev/null
@@ -1,67 +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.app.servertransaction;
-
-import static android.content.Context.DEVICE_ID_DEFAULT;
-
-import static org.junit.Assert.assertEquals;
-
-import android.app.ActivityThread;
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link ConfigurationChangeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:ConfigurationChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class ConfigurationChangeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- // Can't mock final class.
- private final Configuration mConfiguration = new Configuration();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testGetContextToUpdate() {
- final ConfigurationChangeItem item = ConfigurationChangeItem
- .obtain(mConfiguration, DEVICE_ID_DEFAULT);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(ActivityThread.currentApplication(), context);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java b/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
deleted file mode 100644
index ecd75a8..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
+++ /dev/null
@@ -1,99 +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.app.servertransaction;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityThread.ActivityClientRecord;
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.util.ArrayMap;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link DestroyActivityItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:DestroyActivityItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class DestroyActivityItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private PendingTransactionActions mPendingActions;
- @Mock
- private IBinder mActivityToken;
-
- // Can't mock final class.
- private ActivityClientRecord mActivityClientRecord;
-
- private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
- private DestroyActivityItem mItem;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mItem = DestroyActivityItem.obtain(
- mActivityToken, false /* finished */, 123 /* configChanges */);
- mActivityClientRecord = new ActivityClientRecord();
- mActivitiesToBeDestroyed = new ArrayMap<>();
-
- doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
- }
-
- @Test
- public void testPreExecute() {
- mItem.preExecute(mHandler);
-
- assertEquals(1, mActivitiesToBeDestroyed.size());
- assertEquals(mItem, mActivitiesToBeDestroyed.get(mActivityToken));
- }
-
- @Test
- public void testPostExecute() {
- mItem.preExecute(mHandler);
- mItem.postExecute(mHandler, mPendingActions);
-
- assertTrue(mActivitiesToBeDestroyed.isEmpty());
- }
-
- @Test
- public void testExecute() {
- mItem.execute(mHandler, mActivityClientRecord, mPendingActions);
-
- verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
- eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
deleted file mode 100644
index a801a76..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
+++ /dev/null
@@ -1,89 +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.app.servertransaction;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.window.WindowContext;
-import android.window.WindowContextInfo;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowContextInfoChangeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:WindowContextInfoChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowContextInfoChangeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private PendingTransactionActions mPendingActions;
- @Mock
- private IBinder mClientToken;
- @Mock
- private WindowContext mWindowContext;
- // Can't mock final class.
- private final Configuration mConfiguration = new Configuration();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testExecute() {
- final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
- .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
- item.execute(mHandler, mPendingActions);
-
- verify(mHandler).handleWindowContextInfoChanged(mClientToken,
- new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
- }
-
- @Test
- public void testGetContextToUpdate() {
- doReturn(mWindowContext).when(mHandler).getWindowContext(mClientToken);
-
- final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
- .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mWindowContext, context);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
deleted file mode 100644
index cf9935f..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
+++ /dev/null
@@ -1,65 +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.app.servertransaction;
-
-import static org.mockito.Mockito.verify;
-
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowContextWindowRemovalItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowContextWindowRemovalItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private PendingTransactionActions mPendingActions;
- @Mock
- private IBinder mClientToken;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testExecute() {
- final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
- mClientToken);
- item.execute(mHandler, mPendingActions);
-
- verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
deleted file mode 100644
index 4d45daf..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
+++ /dev/null
@@ -1,82 +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.app.servertransaction;
-
-import static org.mockito.Mockito.verify;
-
-import android.app.ClientTransactionHandler;
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-import android.util.MergedConfiguration;
-import android.view.IWindow;
-import android.view.InsetsState;
-import android.window.ClientWindowFrames;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowStateResizeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:WindowStateResizeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowStateResizeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private PendingTransactionActions mPendingActions;
- @Mock
- private IWindow mWindow;
-
- private InsetsState mInsetsState;
- private ClientWindowFrames mFrames;
- private MergedConfiguration mConfiguration;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mInsetsState = new InsetsState();
- mFrames = new ClientWindowFrames();
- mConfiguration = new MergedConfiguration();
- }
-
- @Test
- public void testExecute() throws RemoteException {
- final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
- true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
- true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
- true /* dragResizing */);
- item.execute(mHandler, mPendingActions);
-
- verify(mWindow).resized(mFrames,
- true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
- true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
- true /* dragResizing */);
- }
-}
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index 8308e7c..1617eda 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -20,12 +20,15 @@
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.core.util.forEach
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
+import org.junit.Before
import org.junit.Rule
import kotlin.math.ceil
import kotlin.math.floor
@@ -45,6 +48,19 @@
@get:Rule
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ private lateinit var defaultLookupTables: SparseArray<FontScaleConverter>
+
+ @Before
+ fun setup() {
+ defaultLookupTables = FontScaleConverterFactory.sLookupTables.clone()
+ }
+
+ @After
+ fun teardown() {
+ // Restore the default tables (since some tests will have added extras to the cache)
+ FontScaleConverterFactory.sLookupTables = defaultLookupTables
+ }
+
@Test
fun scale200IsTwiceAtSmallSizes() {
val table = FontScaleConverterFactory.forScale(2F)!!
@@ -245,7 +261,7 @@
}
companion object {
- private const val CONVERSION_TOLERANCE = 0.05f
+ private const val CONVERSION_TOLERANCE = 0.18f
}
}
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index faeae2c..0d1dde3 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.NO_CUTOUT;
import static android.view.DisplayCutout.extractBoundsFromList;
import static android.view.DisplayCutout.fromSpec;
@@ -180,7 +182,7 @@
final int displayHeight = 400;
final float density = 1f;
final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
- density, Insets.NONE);
+ density, Insets.NONE, null);
assertThat(cutout.getCutoutPath(), notNullValue());
}
@@ -191,9 +193,9 @@
final int displayHeight = 400;
final float density = 1f;
final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight,
- density, Insets.NONE).getCutoutPath();
+ density, Insets.NONE, null).getCutoutPath();
final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight,
- density, Insets.NONE).getCutoutPath();
+ density, Insets.NONE, null).getCutoutPath();
assertThat(first, equalTo(second));
}
@@ -203,9 +205,9 @@
final int displayHeight = 400;
final float density = 1f;
final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight,
- density, Insets.NONE).getCutoutPath();
+ density, Insets.NONE, null).getCutoutPath();
final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight,
- density, Insets.NONE).getCutoutPath();
+ density, Insets.NONE, null).getCutoutPath();
assertThat(first, not(equalTo(second)));
}
@@ -216,7 +218,7 @@
final int displayHeight = 400;
final float density = 1f;
final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
- density, Insets.NONE);
+ density, Insets.NONE, null);
assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth()));
assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight()));
assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity()));
@@ -360,63 +362,64 @@
@Test
public void fromSpec_caches() {
Insets waterfallInsets = Insets.of(0, 20, 0, 20);
- DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets);
+ DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets, null);
assertThat(
- fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets),
+ fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, waterfallInsets, null),
sameInstance(cached));
}
@Test
public void fromSpec_wontCacheIfSpecChanges() {
- DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f, Insets.NONE);
+ DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 400, 1f, Insets.NONE, null);
assertThat(
- fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+ fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfScreenWidthChanges() {
- DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f, Insets.NONE);
+ DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 400, 1f, Insets.NONE, null);
assertThat(
- fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+ fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfScreenHeightChanges() {
- DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f, Insets.NONE);
+ DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f, Insets.NONE, null);
assertThat(
- fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+ fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfDensityChanges() {
- DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
+ DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE, null);
assertThat(
- fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE),
+ fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f, Insets.NONE, null),
not(sameInstance(cached)));
}
@Test
public void fromSpec_wontCacheIfWaterfallInsetsChange() {
Insets waterfallInsets = Insets.of(0, 20, 0, 20);
- DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE);
+ DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, Insets.NONE, null);
assertThat(
- fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, waterfallInsets),
+ fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f, waterfallInsets, null),
not(sameInstance(cached)));
}
@Test
public void fromSpec_setsSafeInsets_top() {
- DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f, Insets.NONE);
+ DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z", 200, 400, 2f,
+ Insets.NONE, null);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 0)));
}
@Test
public void fromSpec_setsSafeInsets_top_and_bottom() {
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
- + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f, Insets.NONE);
+ + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f, Insets.NONE, null);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
assertThat(cutout.getBoundingRectsAll(), equalTo(new Rect[]{
ZERO_RECT, new Rect(50, 0, 150, 20),
@@ -426,33 +429,35 @@
@Test
public void fromSpec_setsSafeInsets_waterfallTopBottom() {
- DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(0, 30, 0, 30));
+ DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(0, 30, 0, 30), null);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
}
@Test
public void fromSpec_setsSafeInsets_waterfallLeftRight() {
- DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 0, 30, 0));
+ DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 0, 30, 0), null);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 0, 30, 0)));
}
@Test
public void fromSpec_setsSafeInsets_waterfall_allEdges() {
- DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 30, 30, 30));
+ DisplayCutout cutout = fromSpec("", 200, 400, 2f, Insets.of(30, 30, 30, 30), null);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 30, 30, 30)));
}
@Test
public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallTopBottom() {
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
- + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(0, 30, 0, 30));
+ + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f,
+ Insets.of(0, 30, 0, 30), null);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 30, 0, 30)));
}
@Test
public void fromSpec_setsSafeInsets_cutoutTopBottom_waterfallLeftRight() {
DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
- + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f, Insets.of(30, 0, 30, 0));
+ + "@bottom M -50,0 v -20,0 h 100 v 20 z", 200, 400, 2f,
+ Insets.of(30, 0, 30, 0), null);
assertThat(cutout.getSafeInsets(), equalTo(new Rect(30, 20, 30, 20)));
}
@@ -568,7 +573,84 @@
DisplayCutout rotated = cutout.getRotated(displayH, displayW, ROTATION_90, ROTATION_180);
assertEquals(expected, rotated);
}
+ @Test
+ public void testGetRotatedCutoutWithOverride_top_rot0() {
+ int displayW = 500, displayH = 1000;
+ final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+ BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+ DisplayCutout expected = new DisplayCutout(Insets.of(20, 100, 20, 0),
+ ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+ Insets.of(20, 0, 20, 0), null, sideOverrides);
+ DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+ ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+ Insets.of(20, 0, 20, 0), null, sideOverrides);
+ DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_0);
+ assertEquals(expected, rotated);
+ }
+ @Test
+ public void testGetRotatedCutoutWithOverride_top_rot90() {
+ int displayW = 500, displayH = 1000;
+ final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+ BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+ DisplayCutout expected = new DisplayCutout(Insets.of(0, 20, 0, 75),
+ ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(0, displayW - 75, 100, displayW - 50),
+ Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_90), sideOverrides);
+ DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+ ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+ Insets.of(20, 0, 20, 0), null, sideOverrides);
+ DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_90);
+ assertEquals(expected, rotated);
+ }
+
+ @Test
+ public void testGetRotatedCutoutWithOverride_top_rot180() {
+ int displayW = 500, displayH = 1000;
+ final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+ BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+ DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
+ ZERO_RECT, ZERO_RECT, ZERO_RECT,
+ new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
+ Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180), sideOverrides);
+ DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+ ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+ Insets.of(20, 0, 20, 0), null, sideOverrides);
+ DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_180);
+ assertEquals(expected, rotated);
+ }
+
+ @Test
+ public void testGetRotatedCutoutWithOverride_top_rot270() {
+ int displayW = 500, displayH = 1000;
+ final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+ BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+ DisplayCutout expected = new DisplayCutout(Insets.of(0, 75, 0, 20),
+ ZERO_RECT, new Rect(displayH - 100, 50, displayH - 0, 75), ZERO_RECT, ZERO_RECT,
+ Insets.of(0, 20, 0, 20), createParserInfo(ROTATION_270), sideOverrides);
+ DisplayCutout cutout = new DisplayCutout(Insets.of(20, 100, 20, 0),
+ ZERO_RECT, new Rect(50, 0, 75, 100), ZERO_RECT, ZERO_RECT,
+ Insets.of(20, 0, 20, 0), null, sideOverrides);
+ DisplayCutout rotated = cutout.getRotated(displayW, displayH, ROTATION_0, ROTATION_270);
+ assertEquals(expected, rotated);
+ }
+
+ @Test
+ public void testGetRotatedCutoutWithOverride_top_rot90to180() {
+ int displayW = 500, displayH = 1000;
+ final int[] sideOverrides = new int[] {BOUNDS_POSITION_TOP, BOUNDS_POSITION_BOTTOM,
+ BOUNDS_POSITION_BOTTOM, BOUNDS_POSITION_TOP};
+ DisplayCutout expected = new DisplayCutout(Insets.of(20, 0, 20, 100),
+ ZERO_RECT, ZERO_RECT, ZERO_RECT,
+ new Rect(displayW - 75, displayH - 100, displayW - 50, displayH - 0),
+ Insets.of(20, 0, 20, 0), createParserInfo(ROTATION_180),
+ sideOverrides);
+ DisplayCutout cutout = new DisplayCutout(Insets.of(0, 20, 0, 75),
+ ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(0, displayW - 75, 100, displayW - 50),
+ Insets.of(0, 20, 0, 20), null, sideOverrides);
+ // starting from 90, so the start displayW/H are swapped:
+ DisplayCutout rotated = cutout.getRotated(displayH, displayW, ROTATION_90, ROTATION_180);
+ assertEquals(expected, rotated);
+ }
private static DisplayCutout createCutoutTop() {
return createCutoutWithInsets(0, 100, 0, 0);
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index b30a0c8..cf3eb12 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -659,8 +659,6 @@
ViewRootImpl viewRootImpl = view.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
view.invalidate();
- assertEquals(viewRootImpl.getLastPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NORMAL);
viewRootImpl.notifyInsetsAnimationRunningStateChanged(true);
view.invalidate();
});
@@ -672,6 +670,37 @@
});
}
+
+ /**
+ * Test FrameRateBoostOnTouchEnabled API
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_frameRateBoostOnTouch() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ final WindowManager.LayoutParams attrs = viewRootImpl.mWindowAttributes;
+ assertEquals(attrs.getFrameRateBoostOnTouchEnabled(), true);
+ assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
+ attrs.getFrameRateBoostOnTouchEnabled());
+
+ sInstrumentation.runOnMainSync(() -> {
+ attrs.setFrameRateBoostOnTouchEnabled(false);
+ viewRootImpl.setLayoutParams(attrs, false);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ sInstrumentation.runOnMainSync(() -> {
+ final WindowManager.LayoutParams newAttrs = viewRootImpl.mWindowAttributes;
+ assertEquals(newAttrs.getFrameRateBoostOnTouchEnabled(), false);
+ assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
+ newAttrs.getFrameRateBoostOnTouchEnabled());
+ });
+ }
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index 35ddfdb..e52aa1b 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -153,7 +153,7 @@
final ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
// Ensure main session is created.
- final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final ContentCaptureSession unused = manager.getMainContentCaptureSession();
final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
@@ -167,7 +167,7 @@
final ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
// Ensure main session is created.
- final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final ContentCaptureSession unused = manager.getMainContentCaptureSession();
final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
// Default param does not have FLAG_SECURE set.
@@ -184,7 +184,7 @@
final ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
// Ensure main session is created.
- final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final ContentCaptureSession unused = manager.getMainContentCaptureSession();
// Default param does not have FLAG_SECURE set.
final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams();
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 23b9b9b..4a4c693 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -21,12 +21,19 @@
import static org.testng.Assert.assertThrows;
import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.google.common.collect.ImmutableMap;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
@@ -40,6 +47,7 @@
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.ArrayList;
import java.util.Map;
/**
@@ -195,6 +203,22 @@
}
@Override
+ void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ boolean isDisabled() {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ boolean setDisabled(boolean disabled) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
ContentCaptureSession newChild(ContentCaptureContext context) {
throw new UnsupportedOperationException("should not have been called");
}
@@ -210,20 +234,20 @@
}
@Override
- void internalNotifyViewAppeared(ViewStructureImpl node) {
+ void internalNotifyViewAppeared(final int sessionId, ViewStructureImpl node) {
throw new UnsupportedOperationException("should not have been called");
}
@Override
- void internalNotifyViewDisappeared(AutofillId id) {}
+ void internalNotifyViewDisappeared(final int sessionId, AutofillId id) {}
@Override
- void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
+ void internalNotifyViewTextChanged(final int sessionId, AutofillId id, CharSequence text) {
throw new UnsupportedOperationException("should not have been called");
}
@Override
- public void internalNotifyViewTreeEvent(boolean started) {
+ public void internalNotifyViewTreeEvent(final int sessionId, boolean started) {
if (started) {
mInternalNotifyViewTreeEventStartedCount += 1;
} else {
@@ -242,7 +266,34 @@
}
@Override
- void internalNotifyViewInsetsChanged(Insets viewInsets) {
+ void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ public void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+
+ }
+
+ @Override
+ void internalNotifyViewInsetsChanged(final int sessionId, Insets viewInsets) {
throw new UnsupportedOperationException("should not have been called");
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java
new file mode 100644
index 0000000..f0f3a96
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java
@@ -0,0 +1,530 @@
+/*
+ * 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.view.contentcapture;
+
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.graphics.Insets;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentprotection.ContentProtectionEventProcessor;
+
+import androidx.test.core.app.ApplicationProvider;
+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.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Test for {@link MainContentCaptureSessionV2}.
+ *
+ * <p>Run with: {@code atest
+ * FrameworksCoreTests:android.view.contentcapture.MainContentCaptureSessionV2Test}
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper
+public class MainContentCaptureSessionV2Test {
+
+ private static final int BUFFER_SIZE = 100;
+
+ private static final int REASON = 123;
+
+ private static final ContentCaptureEvent EVENT =
+ new ContentCaptureEvent(/* sessionId= */ 0, TYPE_SESSION_STARTED);
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("com.test.package", "TestClass");
+
+ private static final Context sContext = ApplicationProvider.getApplicationContext();
+
+ private static final ContentCaptureManager.StrippedContext sStrippedContext =
+ new ContentCaptureManager.StrippedContext(sContext);
+
+ private TestableLooper mTestableLooper;
+
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock private IContentCaptureManager mMockSystemServerInterface;
+
+ @Mock private ContentProtectionEventProcessor mMockContentProtectionEventProcessor;
+
+ @Mock private IContentCaptureDirectManager mMockContentCaptureDirectManager;
+
+ @Before
+ public void setup() {
+ mTestableLooper = TestableLooper.get(this);
+ }
+
+ @Test
+ public void onSessionStarted_contentProtectionEnabled_processorCreated() {
+ MainContentCaptureSessionV2 session = createSession();
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+
+ session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
+
+ assertThat(session.mContentProtectionEventProcessor).isNotNull();
+ }
+
+ @Test
+ public void onSessionStarted_contentProtectionDisabled_processorNotCreated() {
+ MainContentCaptureSessionV2 session =
+ createSession(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ false);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
+
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ }
+
+ @Test
+ public void onSessionStarted_contentProtectionNoBuffer_processorNotCreated() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ new ContentCaptureOptions.ContentProtectionOptions(
+ /* enableReceiver= */ true,
+ -BUFFER_SIZE,
+ /* requiredGroups= */ List.of(List.of("a")),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
+ MainContentCaptureSessionV2 session = createSession(options);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
+
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ }
+
+ @Test
+ public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ new ContentCaptureOptions.ContentProtectionOptions(
+ /* enableReceiver= */ true,
+ BUFFER_SIZE,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
+ MainContentCaptureSessionV2 session = createSession(options);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
+
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ }
+
+ @Test
+ public void onSessionStarted_noComponentName_processorNotCreated() {
+ MainContentCaptureSessionV2 session = createSession();
+ session.mComponentName = null;
+
+ session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
+
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ }
+
+ @Test
+ public void sendEvent_contentCaptureDisabled_contentProtectionDisabled() {
+ MainContentCaptureSessionV2 session =
+ createSession(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ false);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ public void sendEvent_contentCaptureDisabled_contentProtectionEnabled() {
+ MainContentCaptureSessionV2 session =
+ createSession(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ true);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
+
+ verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ public void sendEvent_contentCaptureEnabled_contentProtectionDisabled() {
+ MainContentCaptureSessionV2 session =
+ createSession(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ false);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNotNull();
+ assertThat(session.mEvents).containsExactly(EVENT);
+ }
+
+ @Test
+ public void sendEvent_contentCaptureEnabled_contentProtectionEnabled() {
+ MainContentCaptureSessionV2 session = createSession();
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
+
+ verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
+ assertThat(session.mEvents).isNotNull();
+ assertThat(session.mEvents).containsExactly(EVENT);
+ }
+
+ @Test
+ public void sendEvent_contentProtectionEnabled_processorNotCreated() {
+ MainContentCaptureSessionV2 session =
+ createSession(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ true);
+
+ session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ public void flush_contentCaptureDisabled_contentProtectionDisabled() throws Exception {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ false);
+ MainContentCaptureSessionV2 session = createSession(options);
+ session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+ session.flush(REASON);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ assertThat(session.mEvents).containsExactly(EVENT);
+ }
+
+ @Test
+ public void flush_contentCaptureDisabled_contentProtectionEnabled() {
+ MainContentCaptureSessionV2 session =
+ createSession(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ true);
+ session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+ session.flush(REASON);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ assertThat(session.mEvents).containsExactly(EVENT);
+ }
+
+ @Test
+ public void flush_contentCaptureEnabled_contentProtectionDisabled() throws Exception {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ false);
+ MainContentCaptureSessionV2 session = createSession(options);
+ session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+ session.flush(REASON);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isEmpty();
+ assertEventFlushedContentCapture(options);
+ }
+
+ @Test
+ public void flush_contentCaptureEnabled_contentProtectionEnabled() throws Exception {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSessionV2 session = createSession(options);
+ session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+ session.flush(REASON);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isEmpty();
+ assertEventFlushedContentCapture(options);
+ }
+
+ @Test
+ public void destroySession() throws Exception {
+ MainContentCaptureSessionV2 session = createSession();
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.destroySession();
+ mTestableLooper.processAllMessages();
+
+ verify(mMockSystemServerInterface).finishSession(anyInt());
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mDirectServiceInterface).isNull();
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ }
+
+ @Test
+ public void resetSession() {
+ MainContentCaptureSessionV2 session = createSession();
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.resetSession(/* newState= */ 0);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockSystemServerInterface);
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mDirectServiceInterface).isNull();
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_notStarted_ContentCaptureDisabled_ProtectionDisabled() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ false);
+ MainContentCaptureSessionV2 session = createSession(options);
+
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_started_ContentCaptureDisabled_ProtectionDisabled() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ false);
+ MainContentCaptureSessionV2 session = createSession(options);
+
+ session.onSessionStarted(0x2, null);
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_notStarted_ContentCaptureEnabled_ProtectionEnabled() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSessionV2 session = createSession(options);
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_started_ContentCaptureEnabled_ProtectionEnabled()
+ throws RemoteException {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSessionV2 session = createSession(options);
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+ session.onSessionStarted(0x2, null);
+ // Override the processor for interaction verification.
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ // Force flush will happen twice.
+ verify(mMockContentCaptureDirectManager, times(1))
+ .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARING), any());
+ verify(mMockContentCaptureDirectManager, times(1))
+ .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARED), any());
+ // Other than the five view events, there will be two additional tree appearing events.
+ verify(mMockContentProtectionEventProcessor, times(7)).processEvent(any());
+ assertThat(session.mEvents).isEmpty();
+ }
+
+ /** Simulates the regular content capture events sequence. */
+ private void notifyContentCaptureEvents(final MainContentCaptureSessionV2 session) {
+ final ArrayList<Object> events = new ArrayList<>(
+ List.of(
+ prepareView(session),
+ prepareView(session),
+ new AutofillId(0),
+ prepareView(session),
+ Insets.of(0, 0, 0, 0)
+ )
+ );
+
+ final SparseArray<ArrayList<Object>> contentCaptureEvents = new SparseArray<>();
+ contentCaptureEvents.set(session.getId(), events);
+
+ session.notifyContentCaptureEvents(contentCaptureEvents);
+ }
+
+ private View prepareView(final MainContentCaptureSessionV2 session) {
+ final View view = new View(sContext);
+ view.setContentCaptureSession(session);
+ return view;
+ }
+
+ private static ContentCaptureOptions createOptions(
+ boolean enableContentCaptureReceiver,
+ ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) {
+ return new ContentCaptureOptions(
+ /* loggingLevel= */ 0,
+ BUFFER_SIZE,
+ /* idleFlushingFrequencyMs= */ 0,
+ /* textChangeFlushingFrequencyMs= */ 0,
+ /* logHistorySize= */ 0,
+ /* disableFlushForViewTreeAppearing= */ false,
+ enableContentCaptureReceiver,
+ contentProtectionOptions,
+ /* whitelistedComponents= */ null);
+ }
+
+ private static ContentCaptureOptions createOptions(
+ boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) {
+ return createOptions(
+ enableContentCaptureReceiver,
+ new ContentCaptureOptions.ContentProtectionOptions(
+ enableContentProtectionReceiver,
+ BUFFER_SIZE,
+ /* requiredGroups= */ List.of(List.of("a")),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
+ }
+
+ private ContentCaptureManager createManager(ContentCaptureOptions options) {
+ return new ContentCaptureManager(sContext, mMockSystemServerInterface, options);
+ }
+
+ private MainContentCaptureSessionV2 createSession(ContentCaptureManager manager) {
+ final Handler testHandler = Handler.createAsync(mTestableLooper.getLooper());
+ MainContentCaptureSessionV2 session =
+ new MainContentCaptureSessionV2(
+ sStrippedContext,
+ manager,
+ testHandler,
+ testHandler,
+ mMockSystemServerInterface);
+ session.mComponentName = COMPONENT_NAME;
+ return session;
+ }
+
+ private MainContentCaptureSessionV2 createSession(ContentCaptureOptions options) {
+ return createSession(createManager(options));
+ }
+
+ private MainContentCaptureSessionV2 createSession(
+ boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) {
+ return createSession(
+ createOptions(enableContentCaptureReceiver, enableContentProtectionReceiver));
+ }
+
+ private MainContentCaptureSessionV2 createSession() {
+ return createSession(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ }
+
+ private void assertEventFlushedContentCapture(ContentCaptureOptions options) throws Exception {
+ ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class);
+ verify(mMockContentCaptureDirectManager)
+ .sendEvents(captor.capture(), eq(REASON), eq(options));
+
+ assertThat(captor.getValue()).isNotNull();
+ List<ContentCaptureEvent> actual = captor.getValue().getList();
+ assertThat(actual).isNotNull();
+ assertThat(actual).containsExactly(EVENT);
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 68c0693..a709d7b 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -84,6 +84,7 @@
/* progress = */ 0,
/* velocityX = */ 0,
/* velocityY = */ 0,
+ /* triggerBack = */ false,
/* swipeEdge = */ BackEvent.EDGE_LEFT,
/* departingAnimationTarget = */ null);
diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
index a5bbeb5..9292f66 100644
--- a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
+++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
@@ -16,7 +16,6 @@
package android.window.flags;
-import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
import static com.android.window.flags.Flags.taskFragmentSystemOrganizerFlag;
import android.platform.test.annotations.Presubmit;
@@ -39,12 +38,6 @@
public class WindowFlagsTest {
@Test
- public void testSyncWindowConfigUpdateFlag() {
- // No crash when accessing the flag.
- syncWindowConfigUpdateFlag();
- }
-
- @Test
public void testTaskFragmentSystemOrganizerFlag() {
// No crash when accessing the flag.
taskFragmentSystemOrganizerFlag();
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index b6813ff..b209c7c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -30,6 +30,7 @@
import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.CoreMatchers.allOf;
@@ -46,6 +47,7 @@
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
import android.view.View;
import android.widget.RelativeLayout;
@@ -88,7 +90,8 @@
public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
new ActivityTestRule<>(ResolverWrapperActivity.class, false,
false);
-
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void cleanOverrideData() {
sOverrides.reset();
@@ -1156,6 +1159,97 @@
sOverrides.cloneProfileUserHandle)));
}
+ @Test
+ public void testTriggerFromPrivateProfile_withoutWorkProfile() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ markPrivateProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> privateResolvedComponentInfos =
+ createResolvedComponentsForTest(3, sOverrides.privateProfileUserHandle);
+ setupResolverControllers(privateResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+ assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+ onView(withId(R.id.button_once)).check(matches(not(isEnabled())));
+ onView(withId(R.id.button_always)).check(matches(not(isEnabled())));
+ for (ResolvedComponentInfo resolvedInfo : privateResolvedComponentInfos) {
+ assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+ sOverrides.privateProfileUserHandle);
+ }
+ }
+
+ @Test
+ public void testTriggerFromPrivateProfile_withWorkProfilePresent(){
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ ResolverActivity.ENABLE_TABBED_VIEW = false;
+ markPrivateProfileUserAvailable();
+ markWorkProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> privateResolvedComponentInfos =
+ createResolvedComponentsForTest(3, sOverrides.privateProfileUserHandle);
+ setupResolverControllers(privateResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+ onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+ assertEquals(activity.getMultiProfilePagerAdapterCount(), 1);
+ for (ResolvedComponentInfo resolvedInfo : privateResolvedComponentInfos) {
+ assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+ sOverrides.privateProfileUserHandle);
+ }
+ }
+
+ @Test
+ public void testPrivateProfile_triggerFromPrimaryUser_withWorkProfilePresent(){
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ markPrivateProfileUserAvailable();
+ markWorkProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
+ sOverrides.workProfileUserHandle);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ assertThat(activity.getAdapter().getCount(), is(2));
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
+ onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+ for (ResolvedComponentInfo resolvedInfo : personalResolvedComponentInfos) {
+ assertEquals(resolvedInfo.getResolveInfoAt(0).userHandle,
+ activity.getPersonalProfileUserHandle());
+ }
+ }
+
+ @Test
+ public void testPrivateProfile_triggerFromWorkProfile(){
+ mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+ android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+ markPrivateProfileUserAvailable();
+ markWorkProfileUserAvailable();
+ Intent sendIntent = createSendImageIntent();
+
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
+ sOverrides.workProfileUserHandle);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ assertThat(activity.getAdapter().getCount(), is(2));
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
+ onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+ for (ResolvedComponentInfo resolvedInfo : personalResolvedComponentInfos) {
+ assertTrue(resolvedInfo.getResolveInfoAt(0).userHandle.equals(
+ activity.getPersonalProfileUserHandle()) || resolvedInfo.getResolveInfoAt(
+ 0).userHandle.equals(activity.getWorkProfileUserHandle()));
+ }
+ }
+
private Intent createSendImageIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -1237,6 +1331,10 @@
ResolverWrapperActivity.sOverrides.cloneProfileUserHandle = UserHandle.of(11);
}
+ private void markPrivateProfileUserAvailable() {
+ ResolverWrapperActivity.sOverrides.privateProfileUserHandle = UserHandle.of(12);
+ }
+
private void setupResolverControllers(
List<ResolvedComponentInfo> personalResolvedComponentInfos,
List<ResolvedComponentInfo> workResolvedComponentInfos) {
@@ -1256,4 +1354,13 @@
eq(UserHandle.SYSTEM)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
}
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> resolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(resolvedComponentInfos));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index e193de0..862cbd5 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -88,6 +88,10 @@
return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1));
}
+ int getMultiProfilePagerAdapterCount(){
+ return mMultiProfilePagerAdapter.getCount();
+ }
+
@Override
public boolean isVoiceInteraction() {
if (sOverrides.isVoiceInteraction != null) {
@@ -144,6 +148,11 @@
}
@Override
+ protected UserHandle getPrivateProfileUserHandle() {
+ return sOverrides.privateProfileUserHandle;
+ }
+
+ @Override
protected UserHandle getTabOwnerUserHandleForLaunch() {
if (sOverrides.tabOwnerUserHandleForLaunch == null) {
return super.getTabOwnerUserHandleForLaunch();
@@ -176,6 +185,7 @@
public Boolean isVoiceInteraction;
public UserHandle workProfileUserHandle;
public UserHandle cloneProfileUserHandle;
+ public UserHandle privateProfileUserHandle;
public UserHandle tabOwnerUserHandleForLaunch;
public Integer myUserId;
public boolean hasCrossProfileIntents;
@@ -191,6 +201,7 @@
workResolverListController = mock(ResolverListController.class);
workProfileUserHandle = null;
cloneProfileUserHandle = null;
+ privateProfileUserHandle = null;
tabOwnerUserHandleForLaunch = null;
myUserId = null;
hasCrossProfileIntents = true;
diff --git a/core/tests/overlaytests/Android.mk b/core/tests/overlaytests/Android.mk
deleted file mode 100644
index b798d87..0000000
--- a/core/tests/overlaytests/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-include $(call all-subdir-makefiles)
diff --git a/core/tests/overlaytests/host/Android.mk b/core/tests/overlaytests/host/Android.mk
deleted file mode 100644
index d58d939..0000000
--- a/core/tests/overlaytests/host/Android.mk
+++ /dev/null
@@ -1,19 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Include to build test-apps.
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/core/tests/overlaytests/host/test-apps/Android.mk b/core/tests/overlaytests/host/test-apps/Android.mk
deleted file mode 100644
index 5c7187e..0000000
--- a/core/tests/overlaytests/host/test-apps/Android.mk
+++ /dev/null
@@ -1,16 +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.
-
-include $(call all-subdir-makefiles)
-
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp
new file mode 100644
index 0000000..bb7d63e
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.bp
@@ -0,0 +1,57 @@
+// 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.
+
+// Error: Cannot get the name of the license module in the
+// ./Android.bp file.
+// If no such license module exists, please add one there first.
+// Then reset the default_applicable_licenses property below with the license module name.
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_NonPlatformSignatureOverlay",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ aaptflags: [
+ "--custom-package com.android.server.om.hosttest.signature_overlay_bad",
+ ],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_PlatformSignatureStaticOverlay",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ manifest: "static/AndroidManifest.xml",
+ aaptflags: [
+ "--custom-package com.android.server.om.hosttest.signature_overlay_static",
+ ],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_PlatformSignatureOverlay",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.signature_overlay_v1",
+ "--version-code",
+ "1",
+ "--version-name",
+ "v1",
+ ],
+}
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
deleted file mode 100644
index b453cde9..0000000
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
+++ /dev/null
@@ -1,56 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-my_package_prefix := com.android.server.om.hosttest.signature_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_NonPlatformSignatureOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_bad
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureStaticOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_static
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-include $(BUILD_PACKAGE)
-
-my_package_prefix :=
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp
new file mode 100644
index 0000000..ee0c0e5
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.bp
@@ -0,0 +1,97 @@
+// 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.
+
+// Error: Cannot get the name of the license module in the
+// ./Android.bp file.
+// If no such license module exists, please add one there first.
+// Then reset the default_applicable_licenses property below with the license module name.
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_UpdateOverlay",
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ static_libs: ["androidx.test.rules"],
+ aaptflags: ["--no-resource-removal"],
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_FrameworkOverlayV1",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.framework_overlay_v1",
+ "--version-code",
+ "1",
+ "--version-name",
+ "v1",
+ ],
+ resource_dirs: ["framework/v1/res"],
+ manifest: "framework/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_FrameworkOverlayV2",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.framework_overlay_v2",
+ "--version-code",
+ "2",
+ "--version-name",
+ "v2",
+ ],
+ resource_dirs: ["framework/v2/res"],
+ manifest: "framework/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_AppOverlayV1",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.app_overlay_v1",
+ "--version-code",
+ "1",
+ "--version-name",
+ "v1",
+ ],
+ resource_dirs: ["app/v1/res"],
+ manifest: "app/v1/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "OverlayHostTests_AppOverlayV2",
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ aaptflags: [
+ "--custom-package",
+ "com.android.server.om.hosttest.app_overlay_v2",
+ "--version-code",
+ "2",
+ "--version-name",
+ "v2",
+ ],
+ resource_dirs: ["app/v2/res"],
+ manifest: "app/v2/AndroidManifest.xml",
+}
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
deleted file mode 100644
index 77fc887..0000000
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
+++ /dev/null
@@ -1,93 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT_FLAGS := --no-resource-removal
-include $(BUILD_PACKAGE)
-
-my_package_prefix := com.android.server.om.hosttest.framework_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v1/res
-LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v2/res
-LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-my_package_prefix := com.android.server.om.hosttest.app_overlay
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
-LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v1/res
-LOCAL_MANIFEST_FILE := app/v1/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v2/res
-LOCAL_MANIFEST_FILE := app/v2/AndroidManifest.xml
-include $(BUILD_PACKAGE)
-
-my_package_prefix :=
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c4530f6..13d38d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -347,7 +347,9 @@
<!-- Allow IMS service entitlement app to schedule jobs to run when app in background. -->
<allow-in-power-save-except-idle package="com.android.imsserviceentitlement" />
- <!-- Allow device lock controller app to schedule jobs and alarms when app in background,
- otherwise, it may not be able to enforce provision for managed devices. -->
+ <!-- Allow device lock controller app to schedule jobs and alarms, and have network access
+ when app in background; otherwise, it may not be able to enforce provision for managed
+ devices. -->
<allow-in-power-save package="com.android.devicelockcontroller" />
+ <allow-in-data-usage-save package="com.android.devicelockcontroller" />
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 69a6e6d..b9efe65 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -518,6 +518,8 @@
<permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
<!-- Permission required for CTS test - CtsAmbientContextServiceTestCases -->
<permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"/>
+ <!-- Permission required for CTS test - CtsWearableSensingServiceTestCases -->
+ <permission name="android.permission.MANAGE_WEARABLE_SENSING_SERVICE"/>
<!-- Permission required for CTS test - CtsTelephonyProviderTestCases -->
<permission name="android.permission.WRITE_APN_SETTINGS"/>
<!-- Permission required for GTS test - GtsStatsdHostTestCases -->
@@ -573,6 +575,7 @@
<permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
<permission name="android.permission.BIND_WALLPAPER"/>
<permission name="android.permission.CUSTOMIZE_SYSTEM_UI"/>
+ <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
</privapp-permissions>
<privapp-permissions package="com.android.dynsystem">
diff --git a/data/keyboards/Vendor_0428_Product_4001.kl b/data/keyboards/Vendor_0428_Product_4001.kl
new file mode 100644
index 0000000..7d1dd12
--- /dev/null
+++ b/data/keyboards/Vendor_0428_Product_4001.kl
@@ -0,0 +1,27 @@
+# Gravis GamePad Pro USB
+
+# Yellow
+key 0x131 BUTTON_A
+# Green
+key 0x132 BUTTON_B
+# Red
+key 0x130 BUTTON_X
+# Blue
+key 0x133 BUTTON_Y
+
+# Left Upper Shoulder "1"
+key 0x134 BUTTON_L1
+# Right Upper Shoulder "1"
+key 0x135 BUTTON_R1
+# Left Lower Shoulder "2"
+key 0x136 BUTTON_L2
+# Right Lower Shoulder "2"
+key 0x137 BUTTON_R2
+
+# Select & Start
+key 0x138 BUTTON_SELECT
+key 0x139 BUTTON_START
+
+# D-Pad
+axis 0x00 HAT_X
+axis 0x01 HAT_Y
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index ed99501..29cf054 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -55,7 +55,7 @@
// TODO(b/241126279) Introduce constants to better version functionality
@Override
public int getVendorApiLevel() {
- return 4;
+ return 5;
}
@NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java
deleted file mode 100644
index ff49cdc..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/OverlayCreateParams.java
+++ /dev/null
@@ -1,119 +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 androidx.window.extensions.embedding;
-
-import static java.util.Objects.requireNonNull;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-/**
- * The parameter to create an overlay container that retrieved from
- * {@link android.app.ActivityOptions} bundle.
- */
-class OverlayCreateParams {
-
- // TODO(b/295803704): Move them to WM Extensions so that we can reuse in WM Jetpack.
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS =
- "androidx.window.extensions.OverlayCreateParams";
-
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS_TASK_ID =
- "androidx.window.extensions.OverlayCreateParams.taskId";
-
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS_TAG =
- "androidx.window.extensions.OverlayCreateParams.tag";
-
- @VisibleForTesting
- static final String KEY_OVERLAY_CREATE_PARAMS_BOUNDS =
- "androidx.window.extensions.OverlayCreateParams.bounds";
-
- private final int mTaskId;
-
- @NonNull
- private final String mTag;
-
- @NonNull
- private final Rect mBounds;
-
- OverlayCreateParams(int taskId, @NonNull String tag, @NonNull Rect bounds) {
- mTaskId = taskId;
- mTag = requireNonNull(tag);
- mBounds = requireNonNull(bounds);
- }
-
- int getTaskId() {
- return mTaskId;
- }
-
- @NonNull
- String getTag() {
- return mTag;
- }
-
- @NonNull
- Rect getBounds() {
- return mBounds;
- }
-
- @Override
- public int hashCode() {
- int result = mTaskId;
- result = 31 * result + mTag.hashCode();
- result = 31 * result + mBounds.hashCode();
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) return true;
- if (!(obj instanceof OverlayCreateParams thatParams)) return false;
- return mTaskId == thatParams.mTaskId
- && mTag.equals(thatParams.mTag)
- && mBounds.equals(thatParams.mBounds);
- }
-
- @Override
- public String toString() {
- return OverlayCreateParams.class.getSimpleName() + ": {"
- + "taskId=" + mTaskId
- + ", tag=" + mTag
- + ", bounds=" + mBounds
- + "}";
- }
-
- /** Retrieves the {@link OverlayCreateParams} from {@link android.app.ActivityOptions} bundle */
- @Nullable
- static OverlayCreateParams fromBundle(@NonNull Bundle bundle) {
- final Bundle paramsBundle = bundle.getBundle(KEY_OVERLAY_CREATE_PARAMS);
- if (paramsBundle == null) {
- return null;
- }
- final int taskId = paramsBundle.getInt(KEY_OVERLAY_CREATE_PARAMS_TASK_ID);
- final String tag = requireNonNull(paramsBundle.getString(KEY_OVERLAY_CREATE_PARAMS_TAG));
- final Rect bounds = requireNonNull(paramsBundle.getParcelable(
- KEY_OVERLAY_CREATE_PARAMS_BOUNDS, Rect.class));
-
- return new OverlayCreateParams(taskId, tag, bounds);
- }
-}
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 4973a4d..65597de 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -34,6 +34,7 @@
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
@@ -46,6 +47,7 @@
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
+import android.annotation.CallbackExecutor;
import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityOptions;
@@ -62,6 +64,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemProperties;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -84,6 +87,7 @@
import androidx.window.extensions.WindowExtensionsImpl;
import androidx.window.extensions.core.util.function.Consumer;
import androidx.window.extensions.core.util.function.Function;
+import androidx.window.extensions.core.util.function.Predicate;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
@@ -136,6 +140,15 @@
private Function<SplitAttributesCalculatorParams, SplitAttributes> mSplitAttributesCalculator;
/**
+ * A calculator function to compute {@link ActivityStack} attributes in a task, which is called
+ * when there's {@link #onTaskFragmentParentInfoChanged} or folding state changed.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
+ mActivityStackAttributesCalculator;
+
+ /**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
* below it.
* When the app is host of multiple Tasks, there can be multiple splits controlled by the same
@@ -148,8 +161,20 @@
/** Callback to Jetpack to notify about changes to split states. */
@GuardedBy("mLock")
@Nullable
- private Consumer<List<SplitInfo>> mEmbeddingCallback;
+ private Consumer<List<SplitInfo>> mSplitInfoCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
+
+ /**
+ * Stores callbacks to Jetpack to notify about changes to {@link ActivityStack activityStacks}
+ * and corresponding {@link Executor executors} to dispatch the callback.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final ArrayMap<Consumer<List<ActivityStack>>, Executor> mActivityStackCallbacks =
+ new ArrayMap<>();
+
+ private final List<ActivityStack> mLastReportedActivityStacks = new ArrayList<>();
+
private final Handler mHandler;
final Object mLock = new Object();
private final ActivityStartMonitor mActivityStartMonitor;
@@ -273,7 +298,7 @@
}
@Override
- public void unpinTopActivityStack(int taskId){
+ public void unpinTopActivityStack(int taskId) {
synchronized (mLock) {
Log.i(TAG, "Request to unpin top activity stack.");
final TaskContainer task = getTaskContainer(taskId);
@@ -319,13 +344,35 @@
}
}
+ @Override
+ public void setActivityStackAttributesCalculator(
+ @NonNull Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
+ calculator) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ mActivityStackAttributesCalculator = calculator;
+ }
+ }
+
+ @Override
+ public void clearActivityStackAttributesCalculator() {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ mActivityStackAttributesCalculator = null;
+ }
+ }
+
@GuardedBy("mLock")
@Nullable
Function<SplitAttributesCalculatorParams, SplitAttributes> getSplitAttributesCalculator() {
return mSplitAttributesCalculator;
}
- @Override
+ // TODO(b/295993745): remove after we migrate to the bundle approach.
@NonNull
public ActivityOptions setLaunchingActivityStack(@NonNull ActivityOptions options,
@NonNull IBinder token) {
@@ -342,6 +389,7 @@
/**
* Registers the split organizer callback to notify about changes to active splits.
+ *
* @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
* {@link WindowExtensionsImpl#getVendorApiLevel()} 2.
*/
@@ -355,12 +403,14 @@
/**
* Registers the split organizer callback to notify about changes to active splits.
+ *
* @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2
*/
+ @Override
public void setSplitInfoCallback(Consumer<List<SplitInfo>> callback) {
synchronized (mLock) {
- mEmbeddingCallback = callback;
- updateCallbackIfNecessary();
+ mSplitInfoCallback = callback;
+ updateSplitInfoCallbackIfNecessary();
}
}
@@ -370,7 +420,35 @@
@Override
public void clearSplitInfoCallback() {
synchronized (mLock) {
- mEmbeddingCallback = null;
+ mSplitInfoCallback = null;
+ }
+ }
+
+ /**
+ * Registers the callback for the {@link ActivityStack} state change.
+ *
+ * @param executor The executor to dispatch the callback.
+ * @param callback The callback for this {@link ActivityStack} state change.
+ */
+ @Override
+ public void registerActivityStackCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<List<ActivityStack>> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ synchronized (mLock) {
+ mActivityStackCallbacks.put(callback, executor);
+ updateActivityStackCallbackIfNecessary();
+ }
+ }
+
+ /** @see #registerActivityStackCallback(Executor, Consumer) */
+ @Override
+ public void unregisterActivityStackCallback(@NonNull Consumer<List<ActivityStack>> callback) {
+ Objects.requireNonNull(callback);
+
+ synchronized (mLock) {
+ mActivityStackCallbacks.remove(callback);
}
}
@@ -382,13 +460,11 @@
synchronized (mLock) {
// Translate ActivityStack to TaskFragmentContainer.
final List<TaskFragmentContainer> pendingFinishingContainers =
- activityStackTokens.stream()
- .map(token -> {
+ activityStackTokens.stream().map(token -> {
synchronized (mLock) {
return getContainer(token);
}
- }).filter(Objects::nonNull)
- .toList();
+ }).filter(Objects::nonNull).toList();
if (pendingFinishingContainers.isEmpty()) {
return;
@@ -471,6 +547,68 @@
}
}
+ @Override
+ public void updateActivityStackAttributes(@NonNull IBinder activityStackToken,
+ @NonNull ActivityStackAttributes attributes) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return;
+ }
+ Objects.requireNonNull(activityStackToken);
+ Objects.requireNonNull(attributes);
+
+ synchronized (mLock) {
+ final TaskFragmentContainer container = getContainer(activityStackToken);
+ if (container == null) {
+ Log.w(TAG, "Cannot find TaskFragmentContainer for token:" + activityStackToken);
+ return;
+ }
+ if (!container.isOverlay()) {
+ Log.w(TAG, "Updating non-overlay container has not supported yet!");
+ return;
+ }
+
+ final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
+ final WindowContainerTransaction wct = transactionRecord.getTransaction();
+ mPresenter.applyActivityStackAttributes(wct, container, attributes);
+ transactionRecord.apply(false /* shouldApplyIndependently */);
+ }
+ }
+
+ @Override
+ @Nullable
+ public ParentContainerInfo getParentContainerInfo(@NonNull IBinder activityStackToken) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return null;
+ }
+ Objects.requireNonNull(activityStackToken);
+ synchronized (mLock) {
+ final TaskFragmentContainer container = getContainer(activityStackToken);
+ if (container == null) {
+ return null;
+ }
+ final TaskContainer.TaskProperties properties = container.getTaskContainer()
+ .getTaskProperties();
+ return mPresenter.createParentContainerInfoFromTaskProperties(properties);
+ }
+ }
+
+ @Override
+ @Nullable
+ public IBinder getActivityStackToken(@NonNull String tag) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return null;
+ }
+ Objects.requireNonNull(tag);
+ synchronized (mLock) {
+ final TaskFragmentContainer taskFragmentContainer =
+ getContainer(container -> tag.equals(container.getOverlayTag()));
+ if (taskFragmentContainer == null) {
+ return null;
+ }
+ return taskFragmentContainer.getTaskFragmentToken();
+ }
+ }
+
/**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
@@ -539,8 +677,9 @@
/**
* Called when a TaskFragment is created and organized by this organizer.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskFragmentInfo Info of the TaskFragment that is created.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskFragmentInfo Info of the TaskFragment that is created.
*/
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@@ -548,7 +687,7 @@
@VisibleForTesting
@GuardedBy("mLock")
void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentInfo taskFragmentInfo) {
+ @NonNull TaskFragmentInfo taskFragmentInfo) {
final TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container == null) {
return;
@@ -568,8 +707,9 @@
/**
* Called when the status of an organized TaskFragment is changed.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskFragmentInfo Info of the TaskFragment that is changed.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskFragmentInfo Info of the TaskFragment that is changed.
*/
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@@ -639,8 +779,9 @@
/**
* Called when an organized TaskFragment is removed.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskFragmentInfo Info of the TaskFragment that is removed.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskFragmentInfo Info of the TaskFragment that is removed.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -660,14 +801,14 @@
* Called when the parent leaf Task of organized TaskFragments is changed.
* When the leaf Task is changed, the organizer may want to update the TaskFragments in one
* transaction.
- *
+ * <p>
* For case like screen size change, it will trigger {@link #onTaskFragmentParentInfoChanged}
* with new Task bounds, but may not trigger {@link #onTaskFragmentInfoChanged} because there
* can be an override bounds.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskId Id of the parent Task that is changed.
- * @param parentInfo {@link TaskFragmentParentInfo} of the parent Task.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
+ * @param taskId Id of the parent Task that is changed.
+ * @param parentInfo {@link TaskFragmentParentInfo} of the parent Task.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -720,20 +861,20 @@
* original Task. In this case, we need to notify the organizer so that it can check if the
* Activity matches any split rule.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskId The Task that the activity is reparented to.
- * @param activityIntent The intent that the activity is original launched with.
- * @param activityToken If the activity belongs to the same process as the organizer, this
- * will be the actual activity token; if the activity belongs to a
- * different process, the server will generate a temporary token that
- * the organizer can use to reparent the activity through
- * {@link WindowContainerTransaction} if needed.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskId The Task that the activity is reparented to.
+ * @param activityIntent The intent that the activity is original launched with.
+ * @param activityToken If the activity belongs to the same process as the organizer, this
+ * will be the actual activity token; if the activity belongs to a
+ * different process, the server will generate a temporary token that
+ * the organizer can use to reparent the activity through
+ * {@link WindowContainerTransaction} if needed.
*/
@VisibleForTesting
@GuardedBy("mLock")
void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
- int taskId, @NonNull Intent activityIntent,
- @NonNull IBinder activityToken) {
+ int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) {
// If the activity belongs to the current app process, we treat it as a new activity
// launch.
final Activity activity = getActivity(activityToken);
@@ -781,14 +922,15 @@
* Called when the {@link WindowContainerTransaction} created with
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param errorCallbackToken token set in
- * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
- * @param taskFragmentInfo The {@link TaskFragmentInfo}. This could be {@code null} if no
- * TaskFragment created.
- * @param opType The {@link WindowContainerTransaction.HierarchyOp} of the failed
- * transaction operation.
- * @param exception exception from the server side.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param errorCallbackToken token set in
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param taskFragmentInfo The {@link TaskFragmentInfo}. This could be {@code null} if no
+ * TaskFragment created.
+ * @param opType The {@link WindowContainerTransaction.HierarchyOp} of the failed
+ * transaction operation.
+ * @param exception exception from the server side.
*/
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@@ -828,7 +970,9 @@
}
}
- /** Called on receiving {@link #onTaskFragmentVanished} for cleanup. */
+ /**
+ * Called on receiving {@link #onTaskFragmentVanished} for cleanup.
+ */
@GuardedBy("mLock")
private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
@@ -855,11 +999,12 @@
/**
* Checks if the new added activity should be routed to a particular container. It can create a
* new container for the activity and a new split container if necessary.
- * @param activity the activity that is newly added to the Task.
- * @param isOnReparent whether the activity is reparented to the Task instead of new launched.
- * We only support to split as primary for reparented activity for now.
+ *
+ * @param activity the activity that is newly added to the Task.
+ * @param isOnReparent whether the activity is reparented to the Task instead of new launched.
+ * We only support to split as primary for reparented activity for now.
* @return {@code true} if the activity has been handled, such as placed in a TaskFragment, or
- * in a state that the caller shouldn't handle.
+ * in a state that the caller shouldn't handle.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -892,7 +1037,7 @@
final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
if (!isOnReparent && taskContainer != null
&& taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
- != container) {
+ != container) {
// Do not resolve if the launched activity is not the top-most container (excludes
// the pinned and overlay container) in the Task.
return true;
@@ -917,6 +1062,7 @@
/**
* Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
*/
+ @GuardedBy("mLock")
boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity, @Nullable TaskFragmentContainer container,
boolean isOnReparent) {
@@ -1001,7 +1147,7 @@
@GuardedBy("mLock")
@VisibleForTesting
void placeActivityInTopContainer(@NonNull WindowContainerTransaction wct,
- @NonNull Activity activity) {
+ @NonNull Activity activity) {
if (getContainerWithActivity(activity) != null) {
// The activity has already been put in a TaskFragment. This is likely to be done by
// the server when the activity is started.
@@ -1051,7 +1197,7 @@
*/
@GuardedBy("mLock")
private void expandActivity(@NonNull WindowContainerTransaction wct,
- @NonNull Activity activity) {
+ @NonNull Activity activity) {
final TaskFragmentContainer container = getContainerWithActivity(activity);
if (shouldContainerBeExpanded(container)) {
// Make sure that the existing container is expanded.
@@ -1063,7 +1209,9 @@
}
}
- /** Whether the given new launched activity is in a split with a rule matched. */
+ /**
+ * Whether the given new launched activity is in a split with a rule matched.
+ */
// Suppress GuardedBy warning because lint asks to mark this method as
// @GuardedBy(mPresenter.mController.mLock), which is mLock itself
@SuppressWarnings("GuardedBy")
@@ -1121,7 +1269,9 @@
return getSplitRule(primaryActivity, launchedActivity) != null;
}
- /** Finds the activity below the given activity. */
+ /**
+ * Finds the activity below the given activity.
+ */
@VisibleForTesting
@Nullable
@GuardedBy("mLock")
@@ -1172,8 +1322,8 @@
getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity));
if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
&& canReuseContainer(splitRule, splitContainer.getSplitRule(),
- taskProperties.getTaskMetrics(),
- calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
+ taskProperties.getTaskMetrics(),
+ calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
// Can launch in the existing secondary container if the rules share the same
// presentation.
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
@@ -1307,7 +1457,7 @@
* prioritize to split the new activity with it if it is not
* {@code null}.
* @return the {@link TaskFragmentContainer} to start the new activity in. {@code null} if there
- * is no embedding rule matched.
+ * is no embedding rule matched.
*/
@VisibleForTesting
@Nullable
@@ -1412,7 +1562,7 @@
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@Nullable Activity launchingActivity) {
return createEmptyContainer(wct, intent, taskId, new Rect(), launchingActivity,
- null /* overlayTag */);
+ null /* overlayTag */, null /* launchOptions */);
}
/**
@@ -1426,7 +1576,7 @@
TaskFragmentContainer createEmptyContainer(
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@NonNull Rect bounds, @Nullable Activity launchingActivity,
- @Nullable String overlayTag) {
+ @Nullable String overlayTag, @Nullable Bundle launchOptions) {
// We need an activity in the organizer process in the same Task to use as the owner
// activity, as well as to get the Task window info.
final Activity activityInTask;
@@ -1443,7 +1593,8 @@
return null;
}
final TaskFragmentContainer container = newContainer(null /* pendingAppearedActivity */,
- intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag);
+ intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag,
+ launchOptions);
final IBinder taskFragmentToken = container.getTaskFragmentToken();
// Note that taskContainer will not exist before calling #newContainer if the container
// is the first embedded TF in the task.
@@ -1451,7 +1602,7 @@
final Rect taskBounds = taskContainer.getTaskProperties().getTaskMetrics().getBounds();
final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
final int windowingMode = taskContainer
- .getWindowingModeForSplitTaskFragment(sanitizedBounds);
+ .getWindowingModeForTaskFragment(sanitizedBounds);
mPresenter.createTaskFragment(wct, taskFragmentToken, activityInTask.getActivityToken(),
sanitizedBounds, windowingMode);
mPresenter.updateAnimationParams(wct, taskFragmentToken,
@@ -1468,7 +1619,7 @@
*/
@NonNull
private static Rect sanitizeBounds(@NonNull Rect bounds, @NonNull Intent intent,
- @NonNull Rect taskBounds) {
+ @NonNull Rect taskBounds) {
if (bounds.isEmpty()) {
// Don't need to check if the bounds follows the task bounds.
return bounds;
@@ -1507,11 +1658,11 @@
getActivityIntentMinDimensionsPair(primaryActivity, intent));
if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
&& (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics,
- calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
+ calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
// TODO(b/231845476) we should always respect clearTop.
|| !respectClearTop)
&& mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
- null /* secondaryActivity */, intent) != RESULT_EXPAND_FAILED_NO_TF_INFO) {
+ null /* secondaryActivity */, intent) != RESULT_EXPAND_FAILED_NO_TF_INFO) {
// Can launch in the existing secondary container if the rules share the same
// presentation.
return splitContainer.getSecondaryContainer();
@@ -1536,29 +1687,15 @@
TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
// Check pending appeared activity first because there can be a delay for the server
// update.
- for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
- .getTaskFragmentContainers();
- for (int j = containers.size() - 1; j >= 0; j--) {
- final TaskFragmentContainer container = containers.get(j);
- if (container.hasPendingAppearedActivity(activityToken)) {
- return container;
- }
- }
+ TaskFragmentContainer taskFragmentContainer =
+ getContainer(container -> container.hasPendingAppearedActivity(activityToken));
+ if (taskFragmentContainer != null) {
+ return taskFragmentContainer;
}
+
// Check appeared activity if there is no such pending appeared activity.
- for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
- .getTaskFragmentContainers();
- for (int j = containers.size() - 1; j >= 0; j--) {
- final TaskFragmentContainer container = containers.get(j);
- if (container.hasAppearedActivity(activityToken)) {
- return container;
- }
- }
- }
- return null;
+ return getContainer(container -> container.hasAppearedActivity(activityToken));
}
@GuardedBy("mLock")
@@ -1570,43 +1707,49 @@
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
@NonNull Activity activityInTask, int taskId) {
return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
- activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
+ activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
+ null /* launchOptions */);
}
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
@NonNull Activity activityInTask, int taskId) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
- activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
+ activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
+ null /* launchOptions */);
}
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
- @NonNull Activity activityInTask, int taskId,
- @NonNull TaskFragmentContainer pairedPrimaryContainer) {
+ @NonNull Activity activityInTask, int taskId,
+ @NonNull TaskFragmentContainer pairedPrimaryContainer) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
- activityInTask, taskId, pairedPrimaryContainer, null /* tag */);
+ activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
+ null /* launchOptions */);
}
/**
* Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT.
*
- * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
- * @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
- * @param activityInTask activity in the same Task so that we can get the Task bounds
- * if needed.
- * @param taskId parent Task of the new TaskFragment.
- * @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is
- * set, the new container will be added right above it.
- * @param overlayTag The tag for the new created overlay container. It must be
- * needed if {@code isOverlay} is {@code true}. Otherwise,
- * it should be {@code null}.
+ * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
+ * @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
+ * @param activityInTask activity in the same Task so that we can get the Task bounds
+ * if needed.
+ * @param taskId parent Task of the new TaskFragment.
+ * @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is
+ * set, the new container will be added right above it.
+ * @param overlayTag The tag for the new created overlay container. It must be
+ * needed if {@code isOverlay} is {@code true}. Otherwise,
+ * it should be {@code null}.
+ * @param launchOptions The launch options bundle to create a container. Must be
+ * specified for overlay container.
*/
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
- @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
+ @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
+ @Nullable Bundle launchOptions) {
if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be null,");
}
@@ -1615,7 +1758,8 @@
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
- pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag);
+ pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag,
+ launchOptions);
return container;
}
@@ -1640,7 +1784,9 @@
primaryContainer.getTaskContainer().addSplitContainer(splitContainer);
}
- /** Cleanups all the dependencies when the TaskFragment is entering PIP. */
+ /**
+ * Cleanups all the dependencies when the TaskFragment is entering PIP.
+ */
@GuardedBy("mLock")
private void cleanupForEnterPip(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
@@ -1799,16 +1945,27 @@
@SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentContainer container) {
+ @NonNull TaskFragmentContainer container) {
final TaskContainer taskContainer = container.getTaskContainer();
// Dismiss the overlay container if it's the only container in the task and there's no
// direct activity in the parent task.
if (taskContainer.getTaskFragmentContainers().size() == 1
&& !taskContainer.hasDirectActivity()) {
container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+ return;
}
- // TODO(b/295805054): Add the logic to update overlay container
+ if (mActivityStackAttributesCalculator != null) {
+ final ActivityStackAttributesCalculatorParams params =
+ new ActivityStackAttributesCalculatorParams(
+ mPresenter.createParentContainerInfoFromTaskProperties(
+ taskContainer.getTaskProperties()),
+ container.getOverlayTag(),
+ container.getLaunchOptions());
+ final ActivityStackAttributes attributes = mActivityStackAttributesCalculator
+ .apply(params);
+ mPresenter.applyActivityStackAttributes(wct, container, attributes);
+ }
}
/**
@@ -1817,11 +1974,10 @@
* are {@code null}, the {@link SplitAttributes} will be calculated with
* {@link SplitPresenter#computeSplitAttributes}.
*
- * @param splitContainer The {@link SplitContainer} to update
+ * @param splitContainer The {@link SplitContainer} to update
* @param splitAttributes Update with this {@code splitAttributes} if it is not {@code null}.
* Otherwise, use the value calculated by
* {@link SplitPresenter#computeSplitAttributes}
- *
* @return {@code true} if the update succeed. Otherwise, returns {@code false}.
*/
@VisibleForTesting
@@ -1856,7 +2012,9 @@
return true;
}
- /** Whether the given split is the topmost split in the Task. */
+ /**
+ * Whether the given split is the topmost split in the Task.
+ */
private boolean isTopMostSplit(@NonNull SplitContainer splitContainer) {
final List<SplitContainer> splitContainers = splitContainer.getPrimaryContainer()
.getTaskContainer().getSplitContainers();
@@ -1963,7 +2121,9 @@
return true;
}
- /** Whether or not to allow activity in this container to launch placeholder. */
+ /**
+ * Whether or not to allow activity in this container to launch placeholder.
+ */
@GuardedBy("mLock")
private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) {
final TaskFragmentContainer topContainer = container.getTaskContainer()
@@ -1997,8 +2157,9 @@
/**
* Gets the activity options for starting the placeholder activity. In case the placeholder is
* launched when the Task is in the background, we don't want to bring the Task to the front.
- * @param primaryActivity the primary activity to launch the placeholder from.
- * @param isOnCreated whether this happens during the primary activity onCreated.
+ *
+ * @param primaryActivity the primary activity to launch the placeholder from.
+ * @param isOnCreated whether this happens during the primary activity onCreated.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -2070,7 +2231,16 @@
@VisibleForTesting
@GuardedBy("mLock")
void updateCallbackIfNecessary() {
- if (mEmbeddingCallback == null || !readyToReportToClient()) {
+ updateSplitInfoCallbackIfNecessary();
+ updateActivityStackCallbackIfNecessary();
+ }
+
+ /**
+ * Notifies callbacks about changes to split states if necessary.
+ */
+ @GuardedBy("mLock")
+ private void updateSplitInfoCallbackIfNecessary() {
+ if (!readyToReportToClient() || mSplitInfoCallback == null) {
return;
}
final List<SplitInfo> currentSplitStates = getActiveSplitStatesIfStable();
@@ -2079,7 +2249,32 @@
}
mLastReportedSplitStates.clear();
mLastReportedSplitStates.addAll(currentSplitStates);
- mEmbeddingCallback.accept(currentSplitStates);
+ mSplitInfoCallback.accept(currentSplitStates);
+ }
+
+ /**
+ * Notifies callbacks about changes to {@link ActivityStack} states if necessary.
+ */
+ @GuardedBy("mLock")
+ private void updateActivityStackCallbackIfNecessary() {
+ if (!readyToReportToClient() || mActivityStackCallbacks.isEmpty()) {
+ return;
+ }
+ final List<ActivityStack> currentActivityStacks = getActivityStacksIfStable();
+ if (currentActivityStacks == null
+ || mLastReportedActivityStacks.equals(currentActivityStacks)) {
+ return;
+ }
+ mLastReportedActivityStacks.clear();
+ mLastReportedActivityStacks.addAll(currentActivityStacks);
+ // Copy the map in case a callback is removed during the for-loop.
+ final ArrayMap<Consumer<List<ActivityStack>>, Executor> callbacks =
+ new ArrayMap<>(mActivityStackCallbacks);
+ for (int i = callbacks.size() - 1; i >= 0; --i) {
+ final Executor executor = callbacks.valueAt(i);
+ final Consumer<List<ActivityStack>> callback = callbacks.keyAt(i);
+ executor.execute(() -> callback.accept(currentActivityStacks));
+ }
}
/**
@@ -2104,6 +2299,27 @@
}
/**
+ * Returns a list of currently active {@link ActivityStack activityStacks}.
+ *
+ * @return a list of {@link ActivityStack activityStacks} if all the containers are in
+ * a stable state, or {@code null} otherwise.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private List<ActivityStack> getActivityStacksIfStable() {
+ final List<ActivityStack> activityStacks = new ArrayList<>();
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ final List<ActivityStack> taskActivityStacks =
+ mTaskContainers.valueAt(i).getActivityStacksIfStable();
+ if (taskActivityStacks == null) {
+ return null;
+ }
+ activityStacks.addAll(taskActivityStacks);
+ }
+ return activityStacks;
+ }
+
+ /**
* Whether we can now report the split states to the client.
*/
@GuardedBy("mLock")
@@ -2173,11 +2389,18 @@
@Nullable
@GuardedBy("mLock")
TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
+ return getContainer(container -> fragmentToken.equals(container.getTaskFragmentToken()));
+ }
+
+ @Nullable
+ @GuardedBy("mLock")
+ TaskFragmentContainer getContainer(@NonNull Predicate<TaskFragmentContainer> predicate) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
.getTaskFragmentContainers();
- for (TaskFragmentContainer container : containers) {
- if (container.getTaskFragmentToken().equals(fragmentToken)) {
+ for (int j = containers.size() - 1; j >= 0; j--) {
+ final TaskFragmentContainer container = containers.get(j);
+ if (predicate.test(container)) {
return container;
}
}
@@ -2270,6 +2493,7 @@
* container. There is a case when primary containers for placeholders should be retained
* despite the rule configuration to finish primary with secondary - if they are marked as
* 'sticky' and the placeholder was finished when fully overlapping the primary container.
+ *
* @return {@code true} if the associated container should be retained (and not be finished).
*/
// Suppress GuardedBy warning because lint ask to mark this method as
@@ -2345,54 +2569,60 @@
@GuardedBy("mLock")
@Nullable
TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
- @NonNull WindowContainerTransaction wct,
- @NonNull OverlayCreateParams overlayCreateParams, int launchTaskId,
+ @NonNull WindowContainerTransaction wct, @NonNull Bundle options,
@NonNull Intent intent, @NonNull Activity launchActivity) {
- final int taskId = overlayCreateParams.getTaskId();
- if (taskId != launchTaskId) {
- // The task ID doesn't match the launch activity's. Cannot determine the host task
- // to launch the overlay.
- throw new IllegalArgumentException("The task ID of "
- + "OverlayCreateParams#launchingActivity must match the task ID of "
- + "the activity to #startActivity with the activity options that takes "
- + "OverlayCreateParams.");
- }
final List<TaskFragmentContainer> overlayContainers =
getAllOverlayTaskFragmentContainers();
- final String overlayTag = overlayCreateParams.getTag();
+ final String overlayTag = Objects.requireNonNull(options.getString(KEY_OVERLAY_TAG));
// If the requested bounds of OverlayCreateParams are smaller than minimum dimensions
// specified by Intent, expand the overlay container to fill the parent task instead.
- final Rect bounds = overlayCreateParams.getBounds();
- final Size minDimensions = getMinDimensions(intent);
- final boolean shouldExpandContainer = boundsSmallerThanMinDimensions(bounds,
- minDimensions);
+ final ActivityStackAttributesCalculatorParams params =
+ new ActivityStackAttributesCalculatorParams(
+ mPresenter.createParentContainerInfoFromTaskProperties(
+ mPresenter.getTaskProperties(launchActivity)), overlayTag, options);
+ // Fallback to expand the bounds if there's no activityStackAttributes calculator.
+ final Rect relativeBounds = mActivityStackAttributesCalculator != null
+ ? new Rect(mActivityStackAttributesCalculator.apply(params).getRelativeBounds())
+ : new Rect();
+ final boolean shouldExpandContainer = boundsSmallerThanMinDimensions(relativeBounds,
+ getMinDimensions(intent));
+ // Expand the bounds if the requested bounds are smaller than minimum dimensions.
+ if (shouldExpandContainer) {
+ relativeBounds.setEmpty();
+ }
+ final int taskId = getTaskId(launchActivity);
if (!overlayContainers.isEmpty()) {
for (final TaskFragmentContainer overlayContainer : overlayContainers) {
if (!overlayTag.equals(overlayContainer.getOverlayTag())
&& taskId == overlayContainer.getTaskId()) {
// If there's an overlay container with different tag shown in the same
// task, dismiss the existing overlay container.
- overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
- wct, SplitController.this);
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
}
if (overlayTag.equals(overlayContainer.getOverlayTag())
&& taskId != overlayContainer.getTaskId()) {
// If there's an overlay container with same tag in a different task,
// dismiss the overlay container since the tag must be unique per process.
- overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
- wct, SplitController.this);
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
}
if (overlayTag.equals(overlayContainer.getOverlayTag())
&& taskId == overlayContainer.getTaskId()) {
// If there's an overlay container with the same tag and task ID, we treat
// the OverlayCreateParams as the update to the container.
- final Rect taskBounds = overlayContainer.getTaskContainer().getTaskProperties()
- .getTaskMetrics().getBounds();
final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
- final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
+ final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+ final Rect taskBounds = taskContainer.getTaskProperties().getTaskMetrics()
+ .getBounds();
+ final Rect sanitizedBounds = sanitizeBounds(relativeBounds, intent, taskBounds);
+
mPresenter.resizeTaskFragment(wct, overlayToken, sanitizedBounds);
- mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayToken,
+ final int windowingMode = taskContainer
+ .getWindowingModeForTaskFragment(sanitizedBounds);
+ mPresenter.updateWindowingMode(wct, overlayToken, windowingMode);
+ mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayContainer,
!sanitizedBounds.isEmpty());
// We can just return the updated overlay container and don't need to
// check other condition since we only have one OverlayCreateParams, and
@@ -2402,8 +2632,9 @@
}
}
}
- return createEmptyContainer(wct, intent, taskId,
- (shouldExpandContainer ? new Rect() : bounds), launchActivity, overlayTag);
+ // Launch the overlay container to the task with taskId.
+ return createEmptyContainer(wct, intent, taskId, relativeBounds, launchActivity, overlayTag,
+ options);
}
private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
@@ -2504,7 +2735,9 @@
}
}
- /** Executor that posts on the main application thread. */
+ /**
+ * Executor that posts on the main application thread.
+ */
private static class MainThreadExecutor implements Executor {
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -2568,12 +2801,11 @@
final TaskFragmentContainer launchedInTaskFragment;
if (launchingActivity != null) {
final int taskId = getTaskId(launchingActivity);
- final OverlayCreateParams overlayCreateParams =
- OverlayCreateParams.fromBundle(options);
+ final String overlayTag = options.getString(KEY_OVERLAY_TAG);
if (Flags.activityEmbeddingOverlayPresentationFlag()
- && overlayCreateParams != null) {
+ && overlayTag != null) {
launchedInTaskFragment = createOrUpdateOverlayTaskFragmentIfNeeded(wct,
- overlayCreateParams, taskId, intent, launchingActivity);
+ options, intent, launchingActivity);
} else {
launchedInTaskFragment = resolveStartActivityIntent(wct, taskId, intent,
launchingActivity);
@@ -2653,7 +2885,9 @@
&& calculatedSplitAttributes.equals(containerSplitAttributes);
}
- /** Whether the two rules have the same presentation. */
+ /**
+ * Whether the two rules have the same presentation.
+ */
@VisibleForTesting
static boolean areRulesSamePresentation(@NonNull SplitPairRule rule1,
@NonNull SplitPairRule rule2, @NonNull WindowMetrics parentWindowMetrics) {
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 b5c32bb..543570c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -16,6 +16,8 @@
package androidx.window.extensions.embedding;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.PackageManager.MATCH_ALL;
import android.app.Activity;
@@ -187,7 +189,7 @@
final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(secondaryRelBounds);
+ .getWindowingModeForTaskFragment(secondaryRelBounds);
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
primaryActivity.getActivityToken(), secondaryRelBounds, windowingMode);
updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
@@ -259,7 +261,7 @@
if (container == null || container == containerToAvoid) {
container = mController.newContainer(activity, taskId);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(relBounds);
+ .getWindowingModeForTaskFragment(relBounds);
final IBinder reparentActivityToken = activity.getActivityToken();
createTaskFragment(wct, container.getTaskFragmentToken(), reparentActivityToken,
relBounds, windowingMode, reparentActivityToken);
@@ -268,7 +270,7 @@
} else {
resizeTaskFragmentIfRegistered(wct, container, relBounds);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(relBounds);
+ .getWindowingModeForTaskFragment(relBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
}
updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes);
@@ -310,7 +312,7 @@
// Pass in the primary container to make sure it is added right above the primary.
primaryContainer);
final TaskContainer taskContainer = mController.getTaskContainer(taskId);
- final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
+ final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
primaryRelBounds);
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule, splitAttributes);
@@ -347,6 +349,7 @@
&& secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */)
&& !secondaryRelBounds.isEmpty();
+ // TODO(b/243518738): remove usages of XXXIfRegistered.
// If the task fragments are not registered yet, the positions will be updated after they
// are created again.
resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRelBounds);
@@ -357,7 +360,7 @@
// When placeholder is shown in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
}
- final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
+ final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
primaryRelBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
@@ -398,13 +401,13 @@
* Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}
*/
void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentContainer taskFragmentContainer,
+ @NonNull TaskFragmentContainer container,
boolean isolatedNavigationEnabled) {
- if (taskFragmentContainer.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
+ if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
return;
}
- taskFragmentContainer.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
- setTaskFragmentIsolatedNavigation(wct, taskFragmentContainer.getTaskFragmentToken(),
+ container.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
+ setTaskFragmentIsolatedNavigation(wct, container.getTaskFragmentToken(),
isolatedNavigationEnabled);
}
@@ -566,6 +569,15 @@
super.setCompanionTaskFragment(wct, primary, secondary);
}
+ void applyActivityStackAttributes(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer container, @NonNull ActivityStackAttributes attributes) {
+ final Rect bounds = attributes.getRelativeBounds();
+
+ resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
+ updateWindowingMode(wct, container.getTaskFragmentToken(),
+ bounds.isEmpty() ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_MULTI_WINDOW);
+ }
+
/**
* Expands the split container if the current split bounds are smaller than the Activity or
* Intent that is added to the container.
@@ -1084,4 +1096,15 @@
WindowMetrics getTaskWindowMetrics(@NonNull Activity activity) {
return getTaskProperties(activity).getTaskMetrics();
}
+
+ @NonNull
+ ParentContainerInfo createParentContainerInfoFromTaskProperties(
+ @NonNull TaskProperties taskProperties) {
+ final Configuration configuration = taskProperties.getConfiguration();
+ final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
+ .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
+ configuration.windowConfiguration);
+ return new ParentContainerInfo(taskProperties.getTaskMetrics(), configuration,
+ windowLayoutInfo);
+ }
}
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 028e75f..64ad4fa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -145,7 +145,7 @@
* the pair of TaskFragments are stacked due to the limited space.
*/
@WindowingMode
- int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) {
+ int getWindowingModeForTaskFragment(@Nullable Rect taskFragmentBounds) {
// Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it
// will be set to UNDEFINED which will then inherit the Task windowing mode.
if (taskFragmentBounds == null || taskFragmentBounds.isEmpty() || isInPictureInPicture()) {
@@ -443,6 +443,26 @@
return splitStates;
}
+ // TODO(b/317358445): Makes ActivityStack and SplitInfo callback more stable.
+ /**
+ * Returns a list of currently active {@link ActivityStack activityStacks}.
+ *
+ * @return a list of {@link ActivityStack activityStacks} if all the containers are in
+ * a stable state, or {@code null} otherwise.
+ */
+ @Nullable
+ List<ActivityStack> getActivityStacksIfStable() {
+ final List<ActivityStack> activityStacks = new ArrayList<>();
+ for (TaskFragmentContainer container : mContainers) {
+ final ActivityStack activityStack = container.toActivityStackIfStable();
+ if (activityStack == null) {
+ return null;
+ }
+ activityStacks.add(activityStack);
+ }
+ return activityStacks;
+ }
+
/** A wrapper class which contains the information of {@link TaskContainer} */
static final class TaskProperties {
private final int mDisplayId;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index ed8c4f3..810bded 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.util.Size;
import android.window.TaskFragmentAnimationParams;
@@ -105,6 +106,13 @@
@Nullable
private final String mOverlayTag;
+ /**
+ * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
+ * for {@link #isOverlay()} container.
+ */
+ @NonNull
+ private final Bundle mLaunchOptions = new Bundle();
+
/** Indicates whether the container was cleaned up after the last activity was removed. */
private boolean mIsFinished;
@@ -165,7 +173,7 @@
/**
* @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
- * TaskFragmentContainer, String)
+ * TaskFragmentContainer, String, Bundle)
*/
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent,
@@ -173,7 +181,8 @@
@NonNull SplitController controller,
@Nullable TaskFragmentContainer pairedPrimaryContainer) {
this(pendingAppearedActivity, pendingAppearedIntent, taskContainer,
- controller, pairedPrimaryContainer, null /* overlayTag */);
+ controller, pairedPrimaryContainer, null /* overlayTag */,
+ null /* launchOptions */);
}
/**
@@ -181,11 +190,14 @@
* container transaction.
* @param pairedPrimaryContainer when it is set, the new container will be add right above it
* @param overlayTag Sets to indicate this taskFragment is an overlay container
+ * @param launchOptions The launch options to create this container. Must not be
+ * {@code null} for an overlay container
*/
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller,
- @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
+ @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
+ @Nullable Bundle launchOptions) {
if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
|| (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
throw new IllegalArgumentException(
@@ -195,6 +207,12 @@
mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer;
mOverlayTag = overlayTag;
+ if (overlayTag != null) {
+ Objects.requireNonNull(launchOptions);
+ }
+ if (launchOptions != null) {
+ mLaunchOptions.putAll(launchOptions);
+ }
if (pairedPrimaryContainer != null) {
// The TaskFragment will be positioned right above the paired container.
@@ -344,9 +362,7 @@
if (activities == null) {
return null;
}
- // Already checked nullity in collectNonFinishingActivities.
- final Rect bounds = getInfo().getConfiguration().windowConfiguration.getBounds();
- return new ActivityStack(activities, isEmpty(), mToken, bounds, mOverlayTag);
+ return new ActivityStack(activities, isEmpty(), mToken, mOverlayTag);
}
/** Adds the activity that will be reparented to this container. */
@@ -903,14 +919,25 @@
}
/**
- * Returns the tag specified in {@link OverlayCreateParams#getTag()}. {@code null} if this
- * taskFragment container is not an overlay container.
+ * Returns the tag specified in launch options. {@code null} if this taskFragment container is
+ * not an overlay container.
*/
@Nullable
String getOverlayTag() {
return mOverlayTag;
}
+ /**
+ * Returns the options that was used to launch this {@link TaskFragmentContainer}.
+ * {@link Bundle#isEmpty()} means there's no launch option for this container.
+ * <p>
+ * Note that WM Jetpack owns the logic. The WM Extension library must not modify this object.
+ */
+ @NonNull
+ Bundle getLaunchOptions() {
+ return mLaunchOptions;
+ }
+
@Override
public String toString() {
return toString(true /* includeContainersToFinishOnExit */);
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 4c2433f..5ef6a52 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
@@ -16,16 +16,14 @@
package androidx.window.extensions.embedding;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_BOUNDS;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TAG;
-import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TASK_ID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -45,6 +43,7 @@
import static org.mockito.Mockito.never;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -98,9 +97,6 @@
@Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
- private static final OverlayCreateParams TEST_OVERLAY_CREATE_PARAMS =
- new OverlayCreateParams(TASK_ID, "test,", new Rect(0, 0, 200, 200));
-
private SplitController.ActivityStartMonitor mMonitor;
private Intent mIntent;
@@ -165,37 +161,15 @@
}
@Test
- public void testOverlayCreateParamsFromBundle() {
- assertThat(OverlayCreateParams.fromBundle(new Bundle())).isNull();
-
- assertThat(OverlayCreateParams.fromBundle(createOverlayCreateParamsTestBundle()))
- .isEqualTo(TEST_OVERLAY_CREATE_PARAMS);
- }
-
- @Test
public void testStartActivity_overlayFeatureDisabled_notInvokeCreateOverlayContainer() {
mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
- mMonitor.onStartActivity(mActivity, mIntent, createOverlayCreateParamsTestBundle());
+ final Bundle optionsBundle = ActivityOptions.makeBasic().toBundle();
+ optionsBundle.putString(KEY_OVERLAY_TAG, "test");
+ mMonitor.onStartActivity(mActivity, mIntent, optionsBundle);
verify(mSplitController, never()).createOrUpdateOverlayTaskFragmentIfNeeded(any(), any(),
- anyInt(), any(), any());
- }
-
- @NonNull
- private static Bundle createOverlayCreateParamsTestBundle() {
- final Bundle bundle = new Bundle();
-
- final Bundle paramsBundle = new Bundle();
- paramsBundle.putInt(KEY_OVERLAY_CREATE_PARAMS_TASK_ID,
- TEST_OVERLAY_CREATE_PARAMS.getTaskId());
- paramsBundle.putString(KEY_OVERLAY_CREATE_PARAMS_TAG, TEST_OVERLAY_CREATE_PARAMS.getTag());
- paramsBundle.putObject(KEY_OVERLAY_CREATE_PARAMS_BOUNDS,
- TEST_OVERLAY_CREATE_PARAMS.getBounds());
-
- bundle.putBundle(KEY_OVERLAY_CREATE_PARAMS, paramsBundle);
-
- return bundle;
+ any(), any());
}
@Test
@@ -221,19 +195,11 @@
}
@Test
- public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_taskIdNotMatch_throwException() {
- assertThrows("The method must return null due to task mismatch between"
- + " launchingActivity and OverlayCreateParams", IllegalArgumentException.class,
- () -> createOrUpdateOverlayTaskFragmentIfNeeded(
- TEST_OVERLAY_CREATE_PARAMS, TASK_ID + 1));
- }
-
- @Test
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask_dismissOverlay() {
createExistingOverlayContainers();
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID, "test3", new Rect(0, 0, 100, 100)), TASK_ID);
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test3");
assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ " is launched to the same task")
@@ -245,9 +211,9 @@
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_sameTagAnotherTask_dismissOverlay() {
createExistingOverlayContainers();
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID + 2, "test1", new Rect(0, 0, 100, 100)),
- TASK_ID + 2);
+ doReturn(TASK_ID + 2).when(mActivity).getTaskId();
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test1");
assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
+ " is launched with the same tag as an existing overlay container in a different "
@@ -261,9 +227,10 @@
createExistingOverlayContainers();
final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID, "test1", bounds),
- TASK_ID);
+ "test1");
assertWithMessage("overlayContainer1 must be updated since the new overlay container"
+ " is launched with the same tag and task")
@@ -279,9 +246,8 @@
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissMultipleOverlays() {
createExistingOverlayContainers();
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- new OverlayCreateParams(TASK_ID, "test2", new Rect(0, 0, 100, 100)),
- TASK_ID);
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test2");
// OverlayContainer1 is dismissed since new container is launched in the same task with
// different tag. OverlayContainer2 is dismissed since new container is launched with the
@@ -304,8 +270,11 @@
mIntent.setComponent(new ComponentName(ApplicationProvider.getApplicationContext(),
MinimumDimensionActivity.class));
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
@@ -316,24 +285,24 @@
// Call createOrUpdateOverlayTaskFragmentIfNeeded again to check the update case.
clearInvocations(mSplitPresenter);
- createOrUpdateOverlayTaskFragmentIfNeeded(TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
+ verify(mSplitPresenter).updateWindowingMode(mTransaction, overlayToken,
+ WINDOWING_MODE_UNDEFINED);
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayContainer,
false);
- assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
- .containsExactly(overlayContainer);
}
@Test
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_notInTaskBounds_expandOverlay() {
final Rect bounds = new Rect(TASK_BOUNDS);
bounds.offset(10, 10);
- final OverlayCreateParams paramsOutsideTaskBounds = new OverlayCreateParams(TASK_ID,
- "test", bounds);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- paramsOutsideTaskBounds, TASK_ID);
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
@@ -344,26 +313,30 @@
// Call createOrUpdateOverlayTaskFragmentIfNeeded again to check the update case.
clearInvocations(mSplitPresenter);
- createOrUpdateOverlayTaskFragmentIfNeeded(paramsOutsideTaskBounds, TASK_ID);
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
- false);
+ verify(mSplitPresenter).updateWindowingMode(mTransaction,
+ overlayToken, WINDOWING_MODE_UNDEFINED);
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
+ overlayContainer, false);
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
.containsExactly(overlayContainer);
}
@Test
public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_createOverlay() {
- final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
- TEST_OVERLAY_CREATE_PARAMS, TASK_ID);
+ final Rect bounds = new Rect(0, 0, 100, 100);
+ mSplitController.setActivityStackAttributesCalculator(params ->
+ new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
+ final TaskFragmentContainer overlayContainer =
+ createOrUpdateOverlayTaskFragmentIfNeeded("test");
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
.containsExactly(overlayContainer);
assertThat(overlayContainer.getTaskId()).isEqualTo(TASK_ID);
- assertThat(overlayContainer
- .areLastRequestedBoundsEqual(TEST_OVERLAY_CREATE_PARAMS.getBounds())).isTrue();
- assertThat(overlayContainer.getOverlayTag()).isEqualTo(TEST_OVERLAY_CREATE_PARAMS.getTag());
+ assertThat(overlayContainer.areLastRequestedBoundsEqual(bounds)).isTrue();
+ assertThat(overlayContainer.getOverlayTag()).isEqualTo("test");
}
@Test
@@ -416,12 +389,14 @@
@Test
public void testGetTopNonFinishingActivityWithOverlay() {
- createTestOverlayContainer(TASK_ID, "test1");
+ TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test1");
+
final Activity activity = createMockActivity();
final TaskFragmentContainer container = createMockTaskFragmentContainer(activity);
final TaskContainer task = container.getTaskContainer();
- assertThat(task.getTopNonFinishingActivity(true /* includeOverlay */)).isEqualTo(mActivity);
+ assertThat(task.getTopNonFinishingActivity(true /* includeOverlay */))
+ .isEqualTo(overlayContainer.getTopNonFinishingActivity());
assertThat(task.getTopNonFinishingActivity(false /* includeOverlay */)).isEqualTo(activity);
}
@@ -453,15 +428,60 @@
.that(taskContainer.getTaskFragmentContainers()).isEmpty();
}
+ @Test
+ public void testUpdateActivityStackAttributes_nullParams_throwException() {
+ assertThrows(NullPointerException.class, () ->
+ mSplitController.updateActivityStackAttributes(null,
+ new ActivityStackAttributes.Builder().build()));
+
+ assertThrows(NullPointerException.class, () ->
+ mSplitController.updateActivityStackAttributes(new Binder(), null));
+
+ verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+ }
+
+ @Test
+ public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
+ final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
+ mActivity.getTaskId());
+ mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ new ActivityStackAttributes.Builder().build());
+
+ verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+ }
+
+ @Test
+ public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() {
+ final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
+
+ mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ new ActivityStackAttributes.Builder().build());
+
+ verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+ }
+
+ @Test
+ public void testUpdateActivityStackAttributes() {
+ final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+ doNothing().when(mSplitPresenter).applyActivityStackAttributes(any(), any(), any());
+ final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build();
+ final IBinder token = container.getTaskFragmentToken();
+
+ mSplitController.updateActivityStackAttributes(token, attrs);
+
+ verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs));
+ }
+
/**
* A simplified version of {@link SplitController.ActivityStartMonitor
* #createOrUpdateOverlayTaskFragmentIfNeeded}
*/
@Nullable
- private TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
- @NonNull OverlayCreateParams params, int taskId) {
- return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction, params,
- taskId, mIntent, mActivity);
+ private TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(@NonNull String tag) {
+ final Bundle launchOptions = new Bundle();
+ launchOptions.putString(KEY_OVERLAY_TAG, tag);
+ return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction,
+ launchOptions, mIntent, mActivity);
}
/** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
@@ -475,10 +495,11 @@
@NonNull
private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) {
+ Activity activity = createMockActivity();
TaskFragmentContainer overlayContainer = mSplitController.newContainer(
- null /* pendingAppearedActivity */, mIntent, mActivity, taskId,
- null /* pairedPrimaryContainer */, tag);
- setupTaskFragmentInfo(overlayContainer, mActivity);
+ null /* pendingAppearedActivity */, mIntent, activity, taskId,
+ null /* pairedPrimaryContainer */, tag, Bundle.EMPTY);
+ setupTaskFragmentInfo(overlayContainer, activity);
return overlayContainer;
}
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 8c274a2..b60943a 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
@@ -354,7 +354,7 @@
bundle.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
container.getTaskFragmentToken());
monitor.mCurrentIntent = intent;
- doReturn(container).when(mSplitController).getContainer(any());
+ doReturn(container).when(mSplitController).getContainer(any(IBinder.class));
monitor.onStartActivityResult(START_CANCELED, bundle);
assertNull(container.getPendingAppearedIntent());
@@ -590,7 +590,7 @@
assertFalse(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString());
+ anyString(), any());
}
@Test
@@ -753,7 +753,7 @@
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString());
+ anyString(), any());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@@ -796,7 +796,7 @@
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString());
+ anyString(), any());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@@ -1642,7 +1642,7 @@
// We need to set those in case we are not respecting clear top.
// TODO(b/231845476) we should always respect clearTop.
final int windowingMode = mSplitController.getTaskContainer(primaryContainer.getTaskId())
- .getWindowingModeForSplitTaskFragment(TASK_BOUNDS);
+ .getWindowingModeForTaskFragment(TASK_BOUNDS);
primaryContainer.setLastRequestedWindowingMode(windowingMode);
secondaryContainer.setLastRequestedWindowingMode(windowingMode);
primaryContainer.setLastRequestedBounds(getSplitBounds(true /* isPrimary */));
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 7b77235..a5995a3 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
@@ -75,7 +75,7 @@
final Configuration configuration = new Configuration();
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+ taskContainer.getWindowingModeForTaskFragment(splitBounds));
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
@@ -83,7 +83,7 @@
null /* decorSurface */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+ taskContainer.getWindowingModeForTaskFragment(splitBounds));
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
@@ -91,12 +91,12 @@
null /* decorSurface */));
assertEquals(WINDOWING_MODE_FREEFORM,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+ taskContainer.getWindowingModeForTaskFragment(splitBounds));
// Empty bounds means the split pair are stacked, so it should be UNDEFINED which will then
// inherit the Task windowing mode
assertEquals(WINDOWING_MODE_UNDEFINED,
- taskContainer.getWindowingModeForSplitTaskFragment(new Rect()));
+ taskContainer.getWindowingModeForTaskFragment(new Rect()));
}
@Test
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 4511f3b..901d5fa 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -57,3 +57,10 @@
description: "Enables left/right split in portrait"
bug: "291018646"
}
+
+flag {
+ name: "enable_new_bubble_animations"
+ namespace: "multitasking"
+ description: "Enables new animations for expand and collapse for bubbles"
+ bug: "311450609"
+}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 85bf2c1..e4f793c 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -30,8 +30,8 @@
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
- android:paddingStart="16dp">
-
+ android:paddingStart="6dp"
+ android:paddingEnd="8dp">
<ImageView
android:id="@+id/application_icon"
android:layout_width="@dimen/desktop_mode_caption_icon_radius"
@@ -43,7 +43,7 @@
android:id="@+id/application_name"
android:layout_width="0dp"
android:layout_height="20dp"
- android:minWidth="80dp"
+ android:maxWidth="86dp"
android:textAppearance="@android:style/TextAppearance.Material.Title"
android:textSize="14sp"
android:textFontWeight="500"
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 6ad1728..9d4e6f0 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -57,7 +57,7 @@
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"如需退出,请从屏幕底部向上滑动,或点按应用上方的任意位置"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"启动单手模式"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"退出单手模式"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>对话泡的设置"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>消息气泡的设置"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"菜单"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"重新加入叠放"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>:<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -69,22 +69,22 @@
<string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展开“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
<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>
- <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>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"随时控制对话泡"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"点按“管理”按钮,可关闭来自此应用的对话泡"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"关闭消息气泡"</string>
+ <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>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"随时控制消息气泡"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"点按“管理”按钮,可关闭来自此应用的消息气泡"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string>
- <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近没有对话泡"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"此处会显示最近的对话泡和已关闭的对话泡"</string>
- <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使用对话泡聊天"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近没有消息气泡"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"此处会显示最近的消息气泡和已关闭的消息气泡"</string>
+ <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"使用消息气泡聊天"</string>
<string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"新对话会以图标形式显示在屏幕底部的角落中。点按图标即可展开对话,拖动图标即可关闭对话。"</string>
- <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"随时控制对话泡"</string>
- <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"点按此处即可管理哪些应用和对话可以显示对话泡"</string>
+ <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"随时控制消息气泡"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"点按此处即可管理哪些应用和对话可以显示消息气泡"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
- <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭消息气泡。"</string>
<string name="restart_button_description" msgid="4564728020654658478">"点按即可重启此应用,获得更好的视觉体验"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"在“设置”中更改此应用的宽高比"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"更改高宽比"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8f9de61..0a40cea 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -413,6 +413,25 @@
<!-- Height of desktop mode caption for fullscreen tasks. -->
<dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
+ <!-- Required empty space to be visible for partially offscreen tasks. -->
+ <dimen name="freeform_required_visible_empty_space_in_header">48dp</dimen>
+
+ <!-- Required empty space to be visible for partially offscreen tasks on a smaller screen. -->
+ <dimen name="small_screen_required_visible_empty_space_in_header">12dp</dimen>
+
+ <!-- 32dp width back button + 10dp margin -->
+ <dimen name="caption_left_buttons_width">32dp</dimen>
+
+ <!-- (32 dp buttons + 10dp margins) * 3 buttons-->
+ <dimen name="caption_right_buttons_width">126dp</dimen>
+
+ <!-- 2 buttons * 48dp button size. -->
+ <dimen name="desktop_mode_right_edge_buttons_width">96dp</dimen>
+
+ <!-- 22dp padding + 24dp app icon + 16dp expand button.
+ Text varies in size, we will calculate that width separately. -->
+ <dimen name="desktop_mode_app_details_width_minus_text">62dp</dimen>
+
<!-- The width of the maximize menu in desktop mode. -->
<dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java
index e06d3ef..5b0de50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationConstants.java
@@ -21,5 +21,4 @@
*/
class BackAnimationConstants {
static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.20f;
- static final float PROGRESS_COMMIT_THRESHOLD = 0.1f;
}
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 d8c691b..a498236 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
@@ -70,9 +70,11 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -124,6 +126,7 @@
private final Context mContext;
private final ContentResolver mContentResolver;
private final ShellController mShellController;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mShellExecutor;
private final Handler mBgHandler;
@@ -180,7 +183,8 @@
@NonNull @ShellBackgroundThread Handler backgroundHandler,
Context context,
@NonNull BackAnimationBackground backAnimationBackground,
- ShellBackAnimationRegistry shellBackAnimationRegistry) {
+ ShellBackAnimationRegistry shellBackAnimationRegistry,
+ ShellCommandHandler shellCommandHandler) {
this(
shellInit,
shellController,
@@ -190,7 +194,8 @@
context,
context.getContentResolver(),
backAnimationBackground,
- shellBackAnimationRegistry);
+ shellBackAnimationRegistry,
+ shellCommandHandler);
}
@VisibleForTesting
@@ -203,7 +208,8 @@
Context context,
ContentResolver contentResolver,
@NonNull BackAnimationBackground backAnimationBackground,
- ShellBackAnimationRegistry shellBackAnimationRegistry) {
+ ShellBackAnimationRegistry shellBackAnimationRegistry,
+ ShellCommandHandler shellCommandHandler) {
mShellController = shellController;
mShellExecutor = shellExecutor;
mActivityTaskManager = activityTaskManager;
@@ -219,6 +225,7 @@
.build();
mShellBackAnimationRegistry = shellBackAnimationRegistry;
mLatencyTracker = LatencyTracker.getInstance(mContext);
+ mShellCommandHandler = shellCommandHandler;
}
private void onInit() {
@@ -227,6 +234,7 @@
createAdapter();
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BACK_ANIMATION,
this::createExternalInterface, this);
+ mShellCommandHandler.addDumpCallback(this::dump, this);
}
private void setupAnimationDeveloperSettingsObserver(
@@ -968,4 +976,20 @@
};
mBackAnimationAdapter = new BackAnimationAdapter(runner);
}
+
+ /**
+ * Description of current BackAnimationController state.
+ */
+ private void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "BackAnimationController state:");
+ pw.println(prefix + " mEnableAnimations=" + mEnableAnimations.get());
+ pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted);
+ pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
+ pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
+ pw.println(prefix + " mCurrentTracker state:");
+ mCurrentTracker.dump(pw, prefix + " ");
+ pw.println(prefix + " mQueuedTracker state:");
+ mQueuedTracker.dump(pw, prefix + " ");
+ }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
index 215a6cc..30d5edb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
@@ -18,9 +18,9 @@
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.window.BackEvent.EDGE_RIGHT;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
-import static com.android.wm.shell.back.BackAnimationConstants.PROGRESS_COMMIT_THRESHOLD;
import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
@@ -91,7 +91,7 @@
}
};
private static final float MIN_WINDOW_ALPHA = 0.01f;
- private static final float WINDOW_X_SHIFT_DP = 96;
+ private static final float WINDOW_X_SHIFT_DP = 48;
private static final int SCALE_FACTOR = 100;
// TODO(b/264710590): Use the progress commit threshold from ViewConfiguration once it exists.
private static final float TARGET_COMMIT_PROGRESS = 0.5f;
@@ -126,6 +126,8 @@
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mBackInProgress = false;
+ private boolean mIsRightEdge;
+ private boolean mTriggerBack = false;
private PointF mTouchPos = new PointF();
private IRemoteAnimationFinishedCallback mFinishCallback;
@@ -209,6 +211,7 @@
private void finishAnimation() {
if (mEnteringTarget != null) {
+ mTransaction.setCornerRadius(mEnteringTarget.leash, 0);
mEnteringTarget.leash.release();
mEnteringTarget = null;
}
@@ -241,14 +244,15 @@
private void onGestureProgress(@NonNull BackEvent backEvent) {
if (!mBackInProgress) {
+ mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
mBackInProgress = true;
}
mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
float progress = backEvent.getProgress();
- float springProgress = (progress > PROGRESS_COMMIT_THRESHOLD
- ? mapLinear(progress, 0.1f, 1, TARGET_COMMIT_PROGRESS, 1)
+ float springProgress = (mTriggerBack
+ ? mapLinear(progress, 0f, 1, TARGET_COMMIT_PROGRESS, 1)
: mapLinear(progress, 0, 1f, 0, TARGET_COMMIT_PROGRESS)) * SCALE_FACTOR;
mLeavingProgressSpring.animateToFinalPosition(springProgress);
mEnteringProgressSpring.animateToFinalPosition(springProgress);
@@ -312,7 +316,7 @@
transformWithProgress(
mEnteringProgress,
Math.max(
- smoothstep(ENTER_ALPHA_THRESHOLD, 1, mEnteringProgress),
+ smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress),
MIN_WINDOW_ALPHA), /* alpha */
mEnteringTarget.leash,
mEnteringRect,
@@ -337,14 +341,13 @@
mClosingTarget.leash,
mClosingRect,
0,
- mWindowXShift
+ mIsRightEdge ? 0 : mWindowXShift
);
}
}
private void transformWithProgress(float progress, float alpha, SurfaceControl surface,
RectF targetRect, float deltaXMin, float deltaXMax) {
- final float touchY = mTouchPos.y;
final int width = mStartTaskRect.width();
final int height = mStartTaskRect.height();
@@ -376,12 +379,14 @@
private final class Callback extends IOnBackInvokedCallback.Default {
@Override
public void onBackStarted(BackMotionEvent backEvent) {
+ mTriggerBack = backEvent.getTriggerBack();
mProgressAnimator.onBackStarted(backEvent,
CrossActivityBackAnimation.this::onGestureProgress);
}
@Override
public void onBackProgressed(@NonNull BackMotionEvent backEvent) {
+ mTriggerBack = backEvent.getTriggerBack();
mProgressAnimator.onBackProgressed(backEvent);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 4bd56d4..6213f62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -24,6 +24,8 @@
import android.window.BackEvent;
import android.window.BackMotionEvent;
+import java.io.PrintWriter;
+
/**
* Helper class to record the touch location for gesture and generate back events.
*/
@@ -129,6 +131,7 @@
/* progress = */ 0,
/* velocityX = */ 0,
/* velocityY = */ 0,
+ /* triggerBack = */ mTriggerBack,
/* swipeEdge = */ mSwipeEdge,
/* departingAnimationTarget = */ target);
}
@@ -204,6 +207,7 @@
/* progress = */ progress,
/* velocityX = */ mLatestVelocityX,
/* velocityY = */ mLatestVelocityY,
+ /* triggerBack = */ mTriggerBack,
/* swipeEdge = */ mSwipeEdge,
/* departingAnimationTarget = */ null);
}
@@ -219,6 +223,12 @@
mNonLinearFactor = nonLinearFactor;
}
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "TouchTracker state:");
+ pw.println(prefix + " mState=" + mState);
+ pw.println(prefix + " mTriggerBack=" + mTriggerBack);
+ }
+
enum TouchTrackerState {
INITIAL, ACTIVE, FINISHED
}
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 662a5c4..a76bd26 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
@@ -95,7 +95,6 @@
private int mMinimumFlyoutWidthLargeScreen;
private PointF mRestingStackPosition;
- private int[] mPaddings = new int[4];
private boolean mShowingInBubbleBar;
private final Point mBubbleBarPosition = new Point();
@@ -344,46 +343,44 @@
final int pointerTotalHeight = getPointerSize();
final int expandedViewLargeScreenInsetFurthestEdge =
getExpandedViewLargeScreenInsetFurthestEdge(isOverflow);
+ int[] paddings = new int[4];
if (mDeviceConfig.isLargeScreen()) {
// Note:
// If we're in portrait OR if we're a small tablet, then the two insets values will
// be equal. If we're landscape and a large tablet, the two values will be different.
// [left, top, right, bottom]
- mPaddings[0] = onLeft
+ paddings[0] = onLeft
? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight
: expandedViewLargeScreenInsetFurthestEdge;
- mPaddings[1] = 0;
- mPaddings[2] = onLeft
+ paddings[1] = 0;
+ paddings[2] = onLeft
? expandedViewLargeScreenInsetFurthestEdge
: mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight;
// Overflow doesn't show manage button / get padding from it so add padding here
- mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
- return mPaddings;
+ paddings[3] = isOverflow ? mExpandedViewPadding : 0;
+ return paddings;
} else {
int leftPadding = mInsets.left + mExpandedViewPadding;
int rightPadding = mInsets.right + mExpandedViewPadding;
- final float expandedViewWidth = isOverflow
- ? mOverflowWidth
- : mExpandedViewLargeScreenWidth;
if (showBubblesVertically()) {
if (!onLeft) {
rightPadding += mBubbleSize - pointerTotalHeight;
leftPadding += isOverflow
- ? (mPositionRect.width() - rightPadding - expandedViewWidth)
+ ? (mPositionRect.width() - rightPadding - mOverflowWidth)
: 0;
} else {
leftPadding += mBubbleSize - pointerTotalHeight;
rightPadding += isOverflow
- ? (mPositionRect.width() - leftPadding - expandedViewWidth)
+ ? (mPositionRect.width() - leftPadding - mOverflowWidth)
: 0;
}
}
// [left, top, right, bottom]
- mPaddings[0] = leftPadding;
- mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
- mPaddings[2] = rightPadding;
- mPaddings[3] = 0;
- return mPaddings;
+ paddings[0] = leftPadding;
+ paddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
+ paddings[2] = rightPadding;
+ paddings[3] = 0;
+ return paddings;
}
}
@@ -395,7 +392,7 @@
}
/** Gets the y position of the expanded view if it was top-aligned. */
- public float getExpandedViewYTopAligned() {
+ public int getExpandedViewYTopAligned() {
final int top = getAvailableRect().top;
if (showBubblesVertically()) {
return top - mPointerWidth + mExpandedViewPadding;
@@ -413,7 +410,7 @@
return getExpandedViewHeightForLargeScreen();
}
// Subtract top insets because availableRect.height would account for that
- int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
+ int expandedContainerY = getExpandedViewYTopAligned() - getInsets().top;
int paddingTop = showBubblesVertically()
? 0
: mPointerHeight;
@@ -474,11 +471,11 @@
public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) {
boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
float expandedViewHeight = getExpandedViewHeight(bubble);
- float topAlignment = getExpandedViewYTopAligned();
+ int topAlignment = getExpandedViewYTopAligned();
int manageButtonHeight =
isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins;
- // On largescreen portrait bubbles are bottom aligned.
+ // On large screen portrait bubbles are bottom aligned.
if (areBubblesBottomAligned() && expandedViewHeight == MAX_HEIGHT) {
return mPositionRect.bottom - manageButtonHeight
- getExpandedViewHeightForLargeScreen() - mPointerWidth;
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 b7f749e..470a825 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
@@ -1291,7 +1291,7 @@
// We only show user education for conversation bubbles right now
return false;
}
- final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
+ final boolean seen = getPrefBoolean(ManageEducationView.PREF_MANAGED_EDUCATION);
final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
&& mExpandedBubble != null && mExpandedBubble.getExpandedView() != null;
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1342,7 +1342,7 @@
// We only show user education for conversation bubbles right now
return false;
}
- final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
+ final boolean seen = getPrefBoolean(StackEducationView.PREF_STACK_EDUCATION);
final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show stack edu: " + shouldShow);
@@ -2323,7 +2323,8 @@
updateOverflowVisibility();
updatePointerPosition(false /* forIme */);
mExpandedAnimationController.expandFromStack(() -> {
- if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
+ if (mIsExpanded && mExpandedBubble != null
+ && mExpandedBubble.getExpandedView() != null) {
maybeShowManageEdu();
}
updateOverflowDotVisibility(true /* expanding */);
@@ -2384,7 +2385,7 @@
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- if (mExpandedBubble.getExpandedView() != null) {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
mExpandedBubble.getExpandedView().setContentAlpha(0f);
mExpandedBubble.getExpandedView().setBackgroundAlpha(0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 61e17c8..da71b1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -33,15 +33,16 @@
* User education view to highlight the manage button that allows a user to configure the settings
* for the bubble. Shown only the first time a user expands a bubble.
*/
-class ManageEducationView(context: Context, positioner: BubblePositioner) : LinearLayout(context) {
+class ManageEducationView(
+ context: Context,
+ private val positioner: BubblePositioner
+) : LinearLayout(context) {
- private val TAG =
- if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "ManageEducationView"
- else BubbleDebugConfig.TAG_BUBBLES
+ companion object {
+ const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding"
+ private const val ANIMATE_DURATION: Long = 200
+ }
- private val ANIMATE_DURATION: Long = 200
-
- private val positioner: BubblePositioner = positioner
private val manageView by lazy { requireViewById<ViewGroup>(R.id.manage_education_view) }
private val manageButton by lazy { requireViewById<Button>(R.id.manage_button) }
private val gotItButton by lazy { requireViewById<Button>(R.id.got_it) }
@@ -128,7 +129,7 @@
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
}
- setShouldShow(false)
+ updateManageEducationSeen()
}
/**
@@ -218,13 +219,11 @@
}
}
- private fun setShouldShow(shouldShow: Boolean) {
+ private fun updateManageEducationSeen() {
context
.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
.edit()
- .putBoolean(PREF_MANAGED_EDUCATION, !shouldShow)
+ .putBoolean(PREF_MANAGED_EDUCATION, true)
.apply()
}
}
-
-const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 2cabb65..95f1017 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -34,19 +34,15 @@
*/
class StackEducationView(
context: Context,
- positioner: BubblePositioner,
- controller: BubbleController
+ private val positioner: BubblePositioner,
+ private val controller: BubbleController
) : LinearLayout(context) {
- private val TAG =
- if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
- else BubbleDebugConfig.TAG_BUBBLES
-
- private val ANIMATE_DURATION: Long = 200
- private val ANIMATE_DURATION_SHORT: Long = 40
-
- private val positioner: BubblePositioner = positioner
- private val controller: BubbleController = controller
+ companion object {
+ const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"
+ private const val ANIMATE_DURATION: Long = 200
+ private const val ANIMATE_DURATION_SHORT: Long = 40
+ }
private val view by lazy { requireViewById<View>(R.id.stack_education_layout) }
private val titleTextView by lazy { requireViewById<TextView>(R.id.stack_education_title) }
@@ -175,7 +171,7 @@
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
}
- setShouldShow(false)
+ updateStackEducationSeen()
return true
}
@@ -196,13 +192,11 @@
.withEndAction { visibility = GONE }
}
- private fun setShouldShow(shouldShow: Boolean) {
+ private fun updateStackEducationSeen() {
context
.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
.edit()
- .putBoolean(PREF_STACK_EDUCATION, !shouldShow)
+ .putBoolean(PREF_STACK_EDUCATION, true)
.apply()
}
}
-
-const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 5b0239f..7798aa7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -420,7 +420,7 @@
bubbleView.setTranslationY(y);
}
- final float expandedY = mPositioner.getExpandedViewYTopAligned();
+ final int expandedY = mPositioner.getExpandedViewYTopAligned();
final boolean draggedOutEnough =
y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
if (draggedOutEnough != mBubbleDraggedOutEnough) {
@@ -563,7 +563,7 @@
? p.x - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
: p.x + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
animationForChild(child)
- .translationX(fromX, p.y)
+ .translationX(fromX, p.x)
.start();
} else {
float fromY = p.y - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
@@ -634,4 +634,9 @@
.start();
}
}
+
+ /** Returns true if we're in the middle of a collapse or expand animation. */
+ boolean isAnimating() {
+ return mAnimatingCollapse || mAnimatingExpand;
+ }
}
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 f794fef..893a87f 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
@@ -48,12 +48,14 @@
private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100;
+ private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
+ private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
+ private static final int EXPANDED_VIEW_DRAG_ANIMATION_DURATION = 150;
/**
* Additional scale applied to expanded view when it is positioned inside a magnetic target.
*/
- private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f;
- private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
- private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
+ private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.6f;
+ private static final float EXPANDED_VIEW_DRAG_SCALE = 0.5f;
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -72,6 +74,7 @@
private final Context mContext;
private final BubbleBarLayerView mLayerView;
private final BubblePositioner mPositioner;
+ private final int[] mTmpLocation = new int[2];
private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded = false;
@@ -220,6 +223,25 @@
}
/**
+ * Animate the expanded bubble when it is being dragged
+ */
+ public void animateStartDrag() {
+ final BubbleBarExpandedView bbev = getExpandedView();
+ if (bbev == null) {
+ Log.w(TAG, "Trying to animate start drag without a bubble");
+ return;
+ }
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(0f);
+ bbev.animate()
+ .scaleX(EXPANDED_VIEW_DRAG_SCALE)
+ .scaleY(EXPANDED_VIEW_DRAG_SCALE)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION)
+ .start();
+ }
+
+ /**
* Animates dismissal of currently expanded bubble
*
* @param endRunnable a runnable to run at the end of the animation
@@ -261,7 +283,10 @@
.setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION)
.setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
.withStartAction(() -> bbev.setAnimating(true))
- .withEndAction(() -> bbev.setAnimating(false))
+ .withEndAction(() -> {
+ bbev.setAnimating(false);
+ bbev.resetPivot();
+ })
.start();
}
@@ -277,25 +302,52 @@
Log.w(TAG, "Trying to snap the expanded view to target without a bubble");
return;
}
- Point expandedViewCenter = getViewCenterOnScreen(bbev);
-
- // Calculate the difference between the target's center coordinates and the object's.
- // Animating the object's x/y properties by these values will center the object on top
- // of the magnetic target.
- float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x;
- float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y;
// Calculate scale of expanded view so it fits inside the magnetic target
float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight());
- float targetMaxSide = Math.max(target.getTargetView().getWidth(),
- target.getTargetView().getHeight());
- float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide;
+ View targetView = target.getTargetView();
+ float targetMaxSide = Math.max(targetView.getWidth(), targetView.getHeight());
+ // Reduce target size to have some padding between the target and expanded view
+ targetMaxSide *= EXPANDED_VIEW_IN_TARGET_SCALE;
+ float scaleInTarget = targetMaxSide / bbevMaxSide;
+
+ // Scale around the top center of the expanded view. Same as when dragging.
+ bbev.setPivotX(bbev.getWidth() / 2f);
+ bbev.setPivotY(0);
+
+ // When the view animates into the target, it is scaled down with the pivot at center top.
+ // Find the point on the view that would be the center of the view at its final scale.
+ // Once we know that, we can calculate x and y distance from the center of the target view
+ // and use that for the translation animation to ensure that the view at final scale is
+ // placed at the center of the target.
+
+ // Set mTmpLocation to the current location of the view on the screen, taking into account
+ // any scale applied.
+ bbev.getLocationOnScreen(mTmpLocation);
+ // Since pivotX is at the center of the x-axis, even at final scale, center of the view on
+ // x-axis will be the same as the center of the view at current size.
+ // Get scaled width of the view and adjust mTmpLocation so that point on x-axis is at the
+ // center of the view at its current size.
+ float currentWidth = bbev.getWidth() * bbev.getScaleX();
+ mTmpLocation[0] += currentWidth / 2;
+ // Since pivotY is at the top of the view, at final scale, top coordinate of the view
+ // remains the same.
+ // Get height of the view at final scale and adjust mTmpLocation so that point on y-axis is
+ // moved down by half of the height at final scale.
+ float targetHeight = bbev.getHeight() * scaleInTarget;
+ mTmpLocation[1] += targetHeight / 2;
+ // mTmpLocation is now set to the point on the view that will be the center of the view once
+ // scale is applied.
+
+ // Calculate the difference between the target's center coordinates and mTmpLocation
+ float xDiff = target.getCenterOnScreen().x - mTmpLocation[0];
+ float yDiff = target.getCenterOnScreen().y - mTmpLocation[1];
bbev.animate()
.translationX(bbev.getTranslationX() + xDiff)
.translationY(bbev.getTranslationY() + yDiff)
- .scaleX(scale)
- .scaleY(scale)
+ .scaleX(scaleInTarget)
+ .scaleY(scaleInTarget)
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
.setInterpolator(Interpolators.EMPHASIZED)
.withStartAction(() -> bbev.setAnimating(true))
@@ -319,8 +371,8 @@
}
expandedView
.animate()
- .scaleX(1f)
- .scaleY(1f)
+ .scaleX(EXPANDED_VIEW_DRAG_SCALE)
+ .scaleY(EXPANDED_VIEW_DRAG_SCALE)
.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
.setInterpolator(Interpolators.EMPHASIZED)
.withStartAction(() -> expandedView.setAnimating(true))
@@ -385,12 +437,4 @@
final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
return new Size(width, height);
}
-
- private Point getViewCenterOnScreen(View view) {
- Point center = new Point();
- int[] onScreenLocation = view.getLocationOnScreen();
- center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f));
- center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f));
- return center;
- }
}
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 d215450..5e634a2 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
@@ -74,6 +74,9 @@
}
private inner class HandleDragListener : RelativeTouchListener() {
+
+ private var isMoving = false
+
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
return !expandedView.isAnimating
@@ -87,6 +90,10 @@
dx: Float,
dy: Float
) {
+ if (!isMoving) {
+ isMoving = true
+ animationHelper.animateStartDrag()
+ }
expandedView.translationX = expandedViewInitialTranslationX + dx
expandedView.translationY = expandedViewInitialTranslationY + dy
dismissView.show()
@@ -114,6 +121,7 @@
animationHelper.animateToRestPosition()
dismissView.hide()
}
+ isMoving = false
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index afd3b14..81d1399 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -232,6 +232,7 @@
return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton
&& (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
|| taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled)
+ && !taskInfo.appCompatTaskInfo.isSystemFullscreenOverrideEnabled
&& Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
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 3c6bc17..fc97c798 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
@@ -363,7 +363,8 @@
@ShellMainThread ShellExecutor shellExecutor,
@ShellBackgroundThread Handler backgroundHandler,
BackAnimationBackground backAnimationBackground,
- Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry) {
+ Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry,
+ ShellCommandHandler shellCommandHandler) {
if (BackAnimationController.IS_ENABLED) {
return shellBackAnimationRegistry.map(
(animations) ->
@@ -374,7 +375,8 @@
backgroundHandler,
context,
backAnimationBackground,
- animations));
+ animations,
+ shellCommandHandler));
}
return Optional.empty();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index dc82fc1..88949b2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -55,6 +55,26 @@
"persist.wm.debug.desktop_stashing", false);
/**
+ * Flag to indicate whether to apply shadows to windows in desktop mode.
+ */
+ private static final boolean USE_WINDOW_SHADOWS = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_use_window_shadows", true);
+
+ /**
+ * Flag to indicate whether to apply shadows to the focused window in desktop mode.
+ *
+ * Note: this flag is only relevant if USE_WINDOW_SHADOWS is false.
+ */
+ private static final boolean USE_WINDOW_SHADOWS_FOCUSED_WINDOW = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_use_window_shadows_focused_window", false);
+
+ /**
+ * Flag to indicate whether to apply shadows to windows in desktop mode.
+ */
+ private static final boolean USE_ROUNDED_CORNERS = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_use_rounded_corners", true);
+
+ /**
* Return {@code true} is desktop windowing proto 2 is enabled
*/
public static boolean isEnabled() {
@@ -81,4 +101,21 @@
public static boolean isStashingEnabled() {
return IS_STASHING_ENABLED;
}
+
+ /**
+ * Return whether to use window shadows.
+ *
+ * @param isFocusedWindow whether the window to apply shadows to is focused
+ */
+ public static boolean useWindowShadow(boolean isFocusedWindow) {
+ return USE_WINDOW_SHADOWS
+ || (USE_WINDOW_SHADOWS_FOCUSED_WINDOW && isFocusedWindow);
+ }
+
+ /**
+ * Return whether to use rounded corners for windows.
+ */
+ public static boolean useRoundedCorners() {
+ return USE_ROUNDED_CORNERS;
+ }
}
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 144555d..4a1bcaa 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
@@ -721,6 +721,9 @@
finishTransaction: SurfaceControl.Transaction
) {
// Add rounded corners to freeform windows
+ if (!DesktopModeStatus.useRoundedCorners()) {
+ return
+ }
val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
info.changes
.filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
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 ccc3438..e421356 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
@@ -347,7 +347,7 @@
continue;
}
- final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
+ final int pairedTaskId = mSplitTasks.get(taskInfo.taskId, INVALID_TASK_ID);
if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
pairedTaskId)) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
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 ae21c4b..f58aeac 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
@@ -277,11 +277,6 @@
params.token = appToken;
params.packageName = activityInfo.packageName;
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-
- if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
- }
-
params.setTitle("Splash Screen " + title);
return params;
}
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 473deba..af31f5f 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
@@ -17,6 +17,7 @@
package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.wm.shell.transition.Transitions.TransitionObserver;
@@ -35,7 +36,8 @@
/**
* The {@link TransitionObserver} that observes for transitions involving the home
- * activity. It reports transitions to the caller via {@link IHomeTransitionListener}.
+ * activity on the {@link android.view.Display#DEFAULT_DISPLAY} only.
+ * It reports transitions to the caller via {@link IHomeTransitionListener}.
*/
public class HomeTransitionObserver implements TransitionObserver,
RemoteCallable<HomeTransitionObserver> {
@@ -58,6 +60,7 @@
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo == null
+ || taskInfo.displayId != DEFAULT_DISPLAY
|| taskInfo.taskId == -1
|| !taskInfo.isRunning) {
continue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl
index 18716c6..72fba3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IHomeTransitionListener.aidl
@@ -20,12 +20,13 @@
import android.window.TransitionFilter;
/**
- * Listener interface that Launcher attaches to SystemUI to get home activity transition callbacks.
+ * Listener interface that Launcher attaches to SystemUI to get home activity transition callbacks
+ * on the default display.
*/
-interface IHomeTransitionListener {
+oneway interface IHomeTransitionListener {
/**
- * Called when a transition changes the visibility of the home activity.
+ * Called when a transition changes the visibility of the home activity on the default display.
*/
void onHomeVisibilityChanged(in boolean isVisible);
}
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 af69b52..b0d8b47 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
@@ -757,6 +757,10 @@
}
if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
allOccluded = false;
+ } else if (change.hasAllFlags(TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION)) {
+ // Remove the change because it should be invisible in the animation.
+ info.getChanges().remove(i);
+ continue;
}
// The change has already animated by back gesture, don't need to play transition
// animation on it.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index c12ac8b..6e7d11d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
@@ -34,6 +35,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
/**
@@ -84,6 +86,69 @@
mDragPositioningCallback = dragPositioningCallback;
}
+ @Override
+ Rect calculateValidDragArea() {
+ final int leftButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.caption_left_buttons_width);
+
+ // On a smaller screen, don't require as much empty space on screen, as offscreen
+ // drags will be restricted too much.
+ final int requiredEmptySpaceId = mDisplayController.getDisplayContext(mTaskInfo.taskId)
+ .getResources().getConfiguration().smallestScreenWidthDp >= 600
+ ? R.dimen.freeform_required_visible_empty_space_in_header :
+ R.dimen.small_screen_required_visible_empty_space_in_header;
+ final int requiredEmptySpace = loadDimensionPixelSize(mContext.getResources(),
+ requiredEmptySpaceId);
+
+ final int rightButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.caption_right_buttons_width);
+ final int taskWidth = mTaskInfo.configuration.windowConfiguration.getBounds().width();
+ final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ final int displayWidth = layout.width();
+ final Rect stableBounds = new Rect();
+ layout.getStableBounds(stableBounds);
+ return new Rect(
+ determineMinX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace,
+ taskWidth),
+ stableBounds.top,
+ determineMaxX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace, taskWidth,
+ displayWidth),
+ determineMaxY(requiredEmptySpace, stableBounds));
+ }
+
+
+ /**
+ * Determine the lowest x coordinate of a freeform task. Used for restricting drag inputs.
+ */
+ private int determineMinX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+ int taskWidth) {
+ // Do not let apps with < 48dp empty header space go off the left edge at all.
+ if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+ return 0;
+ }
+ return -taskWidth + requiredEmptySpace + rightButtonsWidth;
+ }
+
+ /**
+ * Determine the highest x coordinate of a freeform task. Used for restricting drag inputs.
+ */
+ private int determineMaxX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+ int taskWidth, int displayWidth) {
+ // Do not let apps with < 48dp empty header space go off the right edge at all.
+ if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+ return displayWidth - taskWidth;
+ }
+ return displayWidth - requiredEmptySpace - leftButtonsWidth;
+ }
+
+ /**
+ * Determine the highest y coordinate of a freeform task. Used for restricting drag inputs.
+ */
+ private int determineMaxY(int requiredEmptySpace, Rect stableBounds) {
+ return stableBounds.bottom - requiredEmptySpace;
+ }
+
+
void setDragDetector(DragDetector dragDetector) {
mDragDetector = dragDetector;
mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
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 6ec91e0..2f51278 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
@@ -46,6 +46,7 @@
import android.widget.ImageButton;
import android.window.WindowContainerTransaction;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
@@ -195,46 +196,16 @@
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw) {
- final int shadowRadiusID = taskInfo.isFocused
- ? R.dimen.freeform_decor_shadow_focused_thickness
- : R.dimen.freeform_decor_shadow_unfocused_thickness;
- final boolean isFreeform =
- taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
- final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
-
if (isHandleMenuActive()) {
mHandleMenu.relayout(startT);
}
+ updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw);
+
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- final int windowDecorLayoutId = getDesktopModeWindowDecorLayoutId(
- taskInfo.getWindowingMode());
- mRelayoutParams.reset();
- mRelayoutParams.mRunningTaskInfo = taskInfo;
- mRelayoutParams.mLayoutResId = windowDecorLayoutId;
- mRelayoutParams.mCaptionHeightId = getCaptionHeightId(taskInfo.getWindowingMode());
- mRelayoutParams.mShadowRadiusId = shadowRadiusID;
- mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
- // The configuration used to lay out the window decoration. The system context's config is
- // used when the task density has been overridden to a custom density so that the resources
- // and views of the decoration aren't affected and match the rest of the System UI, if not
- // then just use the task's configuration. A copy is made instead of using the original
- // reference so that the configuration isn't mutated on config changes and diff checks can
- // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
- // See b/301119301.
- // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
- // instead of using a whole Configuration as a parameter.
- final Configuration windowDecorConfig = new Configuration();
- windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
- ? mContext.getResources().getConfiguration() // Use system context.
- : mTaskInfo.configuration); // Use task configuration.
- mRelayoutParams.mWindowDecorConfig = windowDecorConfig;
-
- mRelayoutParams.mCornerRadius =
- (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
@@ -273,6 +244,9 @@
closeMaximizeMenu();
}
+ final boolean isFreeform =
+ taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
if (!isDragResizeable) {
if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
// We still want to track caption bar's exclusion region on a non-resizeable task.
@@ -323,6 +297,45 @@
}
}
+ @VisibleForTesting
+ static void updateRelayoutParams(
+ RelayoutParams relayoutParams,
+ Context context,
+ ActivityManager.RunningTaskInfo taskInfo,
+ boolean applyStartTransactionOnDraw) {
+ relayoutParams.reset();
+ relayoutParams.mRunningTaskInfo = taskInfo;
+ relayoutParams.mLayoutResId =
+ getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
+ relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
+ if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) {
+ relayoutParams.mShadowRadiusId = taskInfo.isFocused
+ ? R.dimen.freeform_decor_shadow_focused_thickness
+ : R.dimen.freeform_decor_shadow_unfocused_thickness;
+ }
+ relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
+ // The configuration used to lay out the window decoration. The system context's config is
+ // used when the task density has been overridden to a custom density so that the resources
+ // and views of the decoration aren't affected and match the rest of the System UI, if not
+ // then just use the task's configuration. A copy is made instead of using the original
+ // reference so that the configuration isn't mutated on config changes and diff checks can
+ // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
+ // See b/301119301.
+ // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
+ // instead of using a whole Configuration as a parameter.
+ final Configuration windowDecorConfig = new Configuration();
+ windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
+ ? context.getResources().getConfiguration() // Use system context.
+ : taskInfo.configuration); // Use task configuration.
+ relayoutParams.mWindowDecorConfig = windowDecorConfig;
+
+ if (DesktopModeStatus.useRoundedCorners()) {
+ relayoutParams.mCornerRadius =
+ (int) ScreenDecorationsUtils.getWindowCornerRadius(context);
+ }
+ }
+
+
private PointF calculateMaximizeMenuPosition() {
final PointF position = new PointF();
final Resources resources = mContext.getResources();
@@ -443,6 +456,66 @@
}
/**
+ * Determine valid drag area for this task based on elements in the app chip.
+ */
+ @Override
+ Rect calculateValidDragArea() {
+ final int appTextWidth = ((DesktopModeAppControlsWindowDecorationViewHolder)
+ mWindowDecorViewHolder).getAppNameTextWidth();
+ final int leftButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_app_details_width_minus_text) + appTextWidth;
+ final int requiredEmptySpace = loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.freeform_required_visible_empty_space_in_header);
+ final int rightButtonsWidth = loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_right_edge_buttons_width);
+ final int taskWidth = mTaskInfo.configuration.windowConfiguration.getBounds().width();
+ final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ final int displayWidth = layout.width();
+ final Rect stableBounds = new Rect();
+ layout.getStableBounds(stableBounds);
+ return new Rect(
+ determineMinX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace,
+ taskWidth),
+ stableBounds.top,
+ determineMaxX(leftButtonsWidth, rightButtonsWidth, requiredEmptySpace,
+ taskWidth, displayWidth),
+ determineMaxY(requiredEmptySpace, stableBounds));
+ }
+
+
+ /**
+ * Determine the lowest x coordinate of a freeform task. Used for restricting drag inputs.
+ */
+ private int determineMinX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+ int taskWidth) {
+ // Do not let apps with < 48dp empty header space go off the left edge at all.
+ if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+ return 0;
+ }
+ return -taskWidth + requiredEmptySpace + rightButtonsWidth;
+ }
+
+ /**
+ * Determine the highest x coordinate of a freeform task. Used for restricting drag inputs.
+ */
+ private int determineMaxX(int leftButtonsWidth, int rightButtonsWidth, int requiredEmptySpace,
+ int taskWidth, int displayWidth) {
+ // Do not let apps with < 48dp empty header space go off the right edge at all.
+ if (leftButtonsWidth + rightButtonsWidth + requiredEmptySpace > taskWidth) {
+ return displayWidth - taskWidth;
+ }
+ return displayWidth - requiredEmptySpace - leftButtonsWidth;
+ }
+
+ /**
+ * Determine the highest y coordinate of a freeform task. Used for restricting drag inputs.
+ */
+ private int determineMaxY(int requiredEmptySpace, Rect stableBounds) {
+ return stableBounds.bottom - requiredEmptySpace;
+ }
+
+
+ /**
* Create and display maximize menu window
*/
void createMaximizeMenu() {
@@ -624,7 +697,7 @@
super.close();
}
- private int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
+ private static int getDesktopModeWindowDecorLayoutId(@WindowingMode int windowingMode) {
return windowingMode == WINDOWING_MODE_FREEFORM
? R.layout.desktop_mode_app_controls_window_decor
: R.layout.desktop_mode_focused_window_decor;
@@ -670,6 +743,10 @@
@Override
int getCaptionHeightId(@WindowingMode int windowingMode) {
+ return getCaptionHeightIdStatic(windowingMode);
+ }
+
+ private static int getCaptionHeightIdStatic(@WindowingMode int windowingMode) {
return windowingMode == WINDOWING_MODE_FULLSCREEN
? R.dimen.desktop_mode_fullscreen_decor_caption_height
: R.dimen.desktop_mode_freeform_decor_caption_height;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index cb0a6c7..677c7f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -162,18 +162,29 @@
/**
* Updates repositionTaskBounds to the final bounds of the task after the drag is finished. If
- * the bounds are outside of the stable bounds, they are shifted to place task at the top of the
- * stable bounds.
+ * the bounds are outside of the valid drag area, the task is shifted back onto the edge of the
+ * valid drag area.
*/
- static void onDragEnd(Rect repositionTaskBounds, Rect taskBoundsAtDragStart, Rect stableBounds,
- PointF repositionStartPoint, float x, float y) {
+ static void onDragEnd(Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
+ PointF repositionStartPoint, float x, float y, Rect validDragArea) {
updateTaskBounds(repositionTaskBounds, taskBoundsAtDragStart, repositionStartPoint,
x, y);
+ snapTaskBoundsIfNecessary(repositionTaskBounds, validDragArea);
+ }
- // If task is outside of stable bounds (in the status bar area), shift the task down.
- if (stableBounds.top > repositionTaskBounds.top) {
- final int yShift = stableBounds.top - repositionTaskBounds.top;
- repositionTaskBounds.offset(0, yShift);
+ private static void snapTaskBoundsIfNecessary(Rect repositionTaskBounds, Rect validDragArea) {
+ // If we were never supplied a valid drag area, do not restrict movement.
+ // Otherwise, we restrict deltas to keep task position inside the Rect.
+ if (validDragArea.width() == 0) return;
+ if (repositionTaskBounds.left < validDragArea.left) {
+ repositionTaskBounds.offset(validDragArea.left - repositionTaskBounds.left, 0);
+ } else if (repositionTaskBounds.left > validDragArea.right) {
+ repositionTaskBounds.offset(validDragArea.right - repositionTaskBounds.left, 0);
+ }
+ if (repositionTaskBounds.top < validDragArea.top) {
+ repositionTaskBounds.offset(0, validDragArea.top - repositionTaskBounds.top);
+ } else if (repositionTaskBounds.top > validDragArea.bottom) {
+ repositionTaskBounds.offset(0, validDragArea.bottom - repositionTaskBounds.top);
}
}
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 3a1ea0e..5d006fb 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
@@ -136,7 +136,8 @@
y)) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
- mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
+ mTaskBoundsAtDragStart, mRepositionStartPoint, x, y,
+ mWindowDecoration.calculateValidDragArea());
wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
mTaskOrganizer.applyTransaction(wct);
}
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 4b55a0c..4363558 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
@@ -152,7 +152,8 @@
mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
y)) {
DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
- mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
+ mTaskBoundsAtDragStart, mRepositionStartPoint, x, y,
+ mDesktopWindowDecoration.calculateValidDragArea());
DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(),
mDesktopWindowDecoration, mRepositionTaskBounds, mTaskOrganizer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 634b755..ee0e31e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -21,6 +21,7 @@
import static android.view.WindowInsets.Type.statusBars;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
@@ -178,6 +179,13 @@
*/
abstract void relayout(RunningTaskInfo taskInfo);
+ /**
+ * Used by the {@link DragPositioningCallback} associated with the implementing class to
+ * enforce drags ending in a valid position. A null result means no restriction.
+ */
+ @Nullable
+ abstract Rect calculateValidDragArea();
+
void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
RelayoutResult<T> outResult) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 589a813..144373f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -42,6 +42,8 @@
private val maximizeWindowButton: ImageButton = rootView.requireViewById(R.id.maximize_window)
private val appNameTextView: TextView = rootView.requireViewById(R.id.application_name)
private val appIconImageView: ImageView = rootView.requireViewById(R.id.application_icon)
+ val appNameTextWidth: Int
+ get() = appNameTextView.width
init {
captionView.setOnTouchListener(onCaptionTouchListener)
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 771876f..9ded6ea 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
@@ -63,6 +63,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellSharedConstants;
@@ -110,6 +111,8 @@
@Mock
private InputManager mInputManager;
+ @Mock
+ private ShellCommandHandler mShellCommandHandler;
private BackAnimationController mController;
private TestableContentResolver mContentResolver;
@@ -145,7 +148,8 @@
mContext,
mContentResolver,
mAnimationBackground,
- mShellBackAnimationRegistry);
+ mShellBackAnimationRegistry,
+ mShellCommandHandler);
mShellInit.init();
mShellExecutor.flushAll();
}
@@ -298,7 +302,8 @@
mContext,
mContentResolver,
mAnimationBackground,
- mShellBackAnimationRegistry);
+ mShellBackAnimationRegistry,
+ mShellCommandHandler);
shellInit.init();
registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
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 874ef80..91503b1 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
@@ -53,6 +53,7 @@
/* progress = */ progress,
/* velocityX = */ 0,
/* velocityY = */ 0,
+ /* triggerBack = */ false,
/* swipeEdge = */ BackEvent.EDGE_LEFT,
/* departingAnimationTarget = */ null);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index c1ff260..60f1d271 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -16,52 +16,51 @@
package com.android.wm.shell.bubbles.animation;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleStackView;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
- private int mDisplayWidth = 500;
- private int mDisplayHeight = 1000;
-
- private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class);
+ private final Semaphore mBubbleRemovedSemaphore = new Semaphore(0);
+ private final Runnable mOnBubbleAnimatedOutAction = mBubbleRemovedSemaphore::release;
ExpandedAnimationController mExpandedController;
private int mStackOffset;
private PointF mExpansionPoint;
private BubblePositioner mPositioner;
- private BubbleStackView.StackViewState mStackViewState = new BubbleStackView.StackViewState();
+ private final BubbleStackView.StackViewState mStackViewState =
+ new BubbleStackView.StackViewState();
- @SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
super.setUp();
@@ -70,15 +69,13 @@
getContext().getSystemService(WindowManager.class));
mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
- new Rect(0, 0, mDisplayWidth, mDisplayHeight));
+ new Rect(0, 0, 500, 1000));
BubbleStackView stackView = mock(BubbleStackView.class);
- when(stackView.getState()).thenReturn(getStackViewState());
mExpandedController = new ExpandedAnimationController(mPositioner,
mOnBubbleAnimatedOutAction,
stackView);
- spyOn(mExpandedController);
addOneMoreThanBubbleLimitBubbles();
mLayout.setActiveController(mExpandedController);
@@ -86,9 +83,18 @@
Resources res = mLayout.getResources();
mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
mExpansionPoint = new PointF(100, 100);
+
+ getStackViewState();
+ when(stackView.getState()).thenAnswer(i -> getStackViewState());
+ waitForMainThread();
}
- public BubbleStackView.StackViewState getStackViewState() {
+ @After
+ public void tearDown() {
+ waitForMainThread();
+ }
+
+ private BubbleStackView.StackViewState getStackViewState() {
mStackViewState.numberOfBubbles = mLayout.getChildCount();
mStackViewState.selectedIndex = 0;
mStackViewState.onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
@@ -96,68 +102,71 @@
}
@Test
- @Ignore
- public void testExpansionAndCollapse() throws InterruptedException {
- Runnable afterExpand = mock(Runnable.class);
- mExpandedController.expandFromStack(afterExpand);
- waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-
+ public void testExpansionAndCollapse() throws Exception {
+ expand();
testBubblesInCorrectExpandedPositions();
- verify(afterExpand).run();
+ waitForMainThread();
- Runnable afterCollapse = mock(Runnable.class);
+ final Semaphore semaphore = new Semaphore(0);
+ Runnable afterCollapse = semaphore::release;
mExpandedController.collapseBackToStack(mExpansionPoint, false, afterCollapse);
- waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-
- testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1);
- verify(afterExpand).run();
+ assertThat(semaphore.tryAcquire(1, 2, TimeUnit.SECONDS)).isTrue();
+ waitForAnimation();
+ testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y);
}
@Test
- @Ignore
- public void testOnChildAdded() throws InterruptedException {
+ public void testOnChildAdded() throws Exception {
expand();
+ waitForMainThread();
// Add another new view and wait for its animation.
final View newView = new FrameLayout(getContext());
mLayout.addView(newView, 0);
- waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+ waitForAnimation();
testBubblesInCorrectExpandedPositions();
}
@Test
- @Ignore
- public void testOnChildRemoved() throws InterruptedException {
+ public void testOnChildRemoved() throws Exception {
expand();
+ waitForMainThread();
- // Remove some views and see if the remaining child views still pass the expansion test.
+ // Remove some views and verify the remaining child views still pass the expansion test.
mLayout.removeView(mViews.get(0));
mLayout.removeView(mViews.get(3));
- waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+ // Removing a view will invoke onBubbleAnimatedOutAction. Block until it gets called twice.
+ assertThat(mBubbleRemovedSemaphore.tryAcquire(2, 2, TimeUnit.SECONDS)).isTrue();
+
+ waitForAnimation();
testBubblesInCorrectExpandedPositions();
}
@Test
- public void testDragBubbleOutDoesntNPE() throws InterruptedException {
+ public void testDragBubbleOutDoesntNPE() {
mExpandedController.onGestureFinished();
mExpandedController.dragBubbleOut(mViews.get(0), 1, 1);
}
/** Expand the stack and wait for animations to finish. */
private void expand() throws InterruptedException {
- mExpandedController.expandFromStack(mock(Runnable.class));
- waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+ final Semaphore semaphore = new Semaphore(0);
+ Runnable afterExpand = semaphore::release;
+
+ mExpandedController.expandFromStack(afterExpand);
+ assertThat(semaphore.tryAcquire(1, TimeUnit.SECONDS)).isTrue();
}
/** Check that children are in the correct positions for being stacked. */
- private void testStackedAtPosition(float x, float y, int offsetMultiplier) {
+ private void testStackedAtPosition(float x, float y) {
// Make sure the rest of the stack moved again, including the first bubble not moving, and
// is stacked to the right now that we're on the right side of the screen.
for (int i = 0; i < mLayout.getChildCount(); i++) {
- assertEquals(x + i * offsetMultiplier * mStackOffset,
- mLayout.getChildAt(i).getTranslationX(), 2f);
- assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f);
+ assertEquals(x, mLayout.getChildAt(i).getTranslationX(), 2f);
+ assertEquals(y + Math.min(i, 1) * mStackOffset, mLayout.getChildAt(i).getTranslationY(),
+ 2f);
assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f);
}
}
@@ -175,4 +184,22 @@
mLayout.getChildAt(i).getTranslationY(), 2f);
}
}
+
+ private void waitForAnimation() throws Exception {
+ final Semaphore semaphore = new Semaphore(0);
+ boolean[] animating = new boolean[]{ true };
+ for (int i = 0; i < 4; i++) {
+ if (animating[0]) {
+ mMainThreadHandler.post(() -> {
+ if (!mExpandedController.isAnimating()) {
+ animating[0] = false;
+ semaphore.release();
+ }
+ });
+ Thread.sleep(500);
+ }
+ }
+ assertThat(semaphore.tryAcquire(1, 2, TimeUnit.SECONDS)).isTrue();
+ waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index 48ae296..2ed5add 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -164,11 +164,17 @@
@Override
public void cancelAllAnimations() {
+ if (mLayout.getChildCount() == 0) {
+ return;
+ }
mMainThreadHandler.post(super::cancelAllAnimations);
}
@Override
public void cancelAnimationsOnView(View view) {
+ if (mLayout.getChildCount() == 0) {
+ return;
+ }
mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view));
}
@@ -221,6 +227,9 @@
@Override
protected void startPathAnimation() {
+ if (mLayout.getChildCount() == 0) {
+ return;
+ }
mMainThreadHandler.post(super::startPathAnimation);
}
}
@@ -322,4 +331,9 @@
e.printStackTrace();
}
}
+
+ /** Waits for the main thread to finish processing all pending runnables. */
+ public void waitForMainThread() {
+ runOnMainThreadAndBlock(() -> {});
+ }
}
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 18fcdd0..77667ca 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
@@ -16,16 +16,24 @@
package com.android.wm.shell.windowdecor;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.os.Handler;
+import android.os.SystemProperties;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
@@ -34,14 +42,17 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -57,6 +68,13 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DesktopModeWindowDecorationTests extends ShellTestCase {
+ private static final String USE_WINDOW_SHADOWS_SYSPROP_KEY =
+ "persist.wm.debug.desktop_use_window_shadows";
+ private static final String FOCUSED_USE_WINDOW_SHADOWS_SYSPROP_KEY =
+ "persist.wm.debug.desktop_use_window_shadows_focused_window";
+ private static final String USE_ROUNDED_CORNERS_SYSPROP_KEY =
+ "persist.wm.debug.desktop_use_rounded_corners";
+
@Mock
private DisplayController mMockDisplayController;
@Mock
@@ -79,14 +97,29 @@
private SurfaceControlViewHost mMockSurfaceControlViewHost;
@Mock
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+ @Mock
+ private TypedArray mMockRoundedCornersRadiusArray;
private final Configuration mConfiguration = new Configuration();
+ private TestableContext mTestableContext;
+
+ /** Set up run before test class. */
+ @BeforeClass
+ public static void setUpClass() {
+ // Reset the sysprop settings before running the test.
+ SystemProperties.set(USE_WINDOW_SHADOWS_SYSPROP_KEY, "");
+ SystemProperties.set(FOCUSED_USE_WINDOW_SHADOWS_SYSPROP_KEY, "");
+ SystemProperties.set(USE_ROUNDED_CORNERS_SYSPROP_KEY, "");
+ }
+
@Before
public void setUp() {
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
any(), any(), any());
doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
+ mTestableContext = new TestableContext(mContext);
+ mTestableContext.ensureTestableResources();
}
@Test
@@ -105,6 +138,50 @@
}
+ @Test
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreEnabled() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true);
+
+ assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersAreEnabled() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ fillRoundedCornersResources(/* fillValue= */ 30);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true);
+
+ assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
+ }
+
+ private void fillRoundedCornersResources(int fillValue) {
+ when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt()))
+ .thenReturn(fillValue);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.array.config_roundedCornerRadiusArray, mMockRoundedCornersRadiusArray);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.dimen.rounded_corner_radius, fillValue);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.array.config_roundedCornerTopRadiusArray, mMockRoundedCornersRadiusArray);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.dimen.rounded_corner_radius_top, fillValue);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.array.config_roundedCornerBottomRadiusArray, mMockRoundedCornersRadiusArray);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.dimen.rounded_corner_radius_bottom, fillValue);
+ }
+
+
private DesktopModeWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo) {
return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index 5c0e04a..e60be71 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -181,6 +181,26 @@
}
@Test
+ fun testDragEndSnapsTaskBoundsWhenOutsideValidDragArea() {
+ val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
+ val repositionTaskBounds = Rect(STARTING_BOUNDS)
+ val validDragArea = Rect(DISPLAY_BOUNDS.left - 100,
+ STABLE_BOUNDS.top,
+ DISPLAY_BOUNDS.right - 100,
+ DISPLAY_BOUNDS.bottom - 100)
+
+ DragPositioningCallbackUtility.onDragEnd(repositionTaskBounds, STARTING_BOUNDS,
+ startingPoint, startingPoint.x - 1000, (DISPLAY_BOUNDS.bottom + 1000).toFloat(),
+ validDragArea)
+ assertThat(repositionTaskBounds.left).isEqualTo(validDragArea.left)
+ assertThat(repositionTaskBounds.top).isEqualTo(validDragArea.bottom)
+ assertThat(repositionTaskBounds.right)
+ .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
+ assertThat(repositionTaskBounds.bottom)
+ .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
+ }
+
+ @Test
fun testChangeBounds_toDisallowedBounds_freezesAtLimit() {
var hasMoved = false
val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index add78b2..2ce49cf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -103,6 +103,7 @@
configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
configuration.windowConfiguration.displayRotation = ROTATION_90
}
+ `when`(mockWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
mockWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
@@ -660,6 +661,38 @@
}
@Test
+ fun testDragResize_drag_taskPositionedInValidDragArea() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED, // drag
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ val newX = VALID_DRAG_AREA.left - 500f
+ val newY = VALID_DRAG_AREA.bottom + 500f
+ taskPositioner.onDragPositioningMove(
+ newX,
+ newY
+ )
+ verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
+
+ taskPositioner.onDragPositioningEnd(
+ newX,
+ newY
+ )
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds.top ==
+ VALID_DRAG_AREA.bottom &&
+ change.configuration.windowConfiguration.bounds.left ==
+ VALID_DRAG_AREA.left
+ }
+ })
+ }
+
+ @Test
fun testDragResize_drag_updatesStableBoundsOnRotate() {
// Test landscape stable bounds
performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
@@ -761,5 +794,11 @@
DISPLAY_BOUNDS.bottom,
DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
)
+ private val VALID_DRAG_AREA = Rect(
+ DISPLAY_BOUNDS.left - 100,
+ STABLE_BOUNDS_LANDSCAPE.top,
+ DISPLAY_BOUNDS.right - 100,
+ DISPLAY_BOUNDS.bottom - 100
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index a70ebf1..a759b53 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -118,6 +118,7 @@
configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
configuration.windowConfiguration.displayRotation = ROTATION_90
}
+ `when`(mockDesktopWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
mockDesktopWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
@@ -379,6 +380,38 @@
}
@Test
+ fun testDragResize_drag_taskPositionedInValidDragArea() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED, // drag
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ val newX = VALID_DRAG_AREA.left - 500f
+ val newY = VALID_DRAG_AREA.bottom + 500f
+ taskPositioner.onDragPositioningMove(
+ newX,
+ newY
+ )
+ verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
+
+ taskPositioner.onDragPositioningEnd(
+ newX,
+ newY
+ )
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds.top ==
+ VALID_DRAG_AREA.bottom &&
+ change.configuration.windowConfiguration.bounds.left ==
+ VALID_DRAG_AREA.left
+ }
+ })
+ }
+
+ @Test
fun testDragResize_drag_updatesStableBoundsOnRotate() {
// Test landscape stable bounds
performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
@@ -470,5 +503,11 @@
DISPLAY_BOUNDS.bottom,
DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
)
+ private val VALID_DRAG_AREA = Rect(
+ DISPLAY_BOUNDS.left - 100,
+ STABLE_BOUNDS_LANDSCAPE.top,
+ DISPLAY_BOUNDS.right - 100,
+ DISPLAY_BOUNDS.bottom - 100
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 8e42f74..fe508e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -702,6 +702,11 @@
relayout(taskInfo, false /* applyStartTransactionOnDraw */);
}
+ @Override
+ Rect calculateValidDragArea() {
+ return null;
+ }
+
void relayout(ActivityManager.RunningTaskInfo taskInfo,
boolean applyStartTransactionOnDraw) {
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt
index f75ccea..d8650e1 100644
--- a/libs/incident/libincident.map.txt
+++ b/libs/incident/libincident.map.txt
@@ -1,15 +1,15 @@
LIBINCIDENT {
global:
- AIncidentReportArgs_init; # systemapi # introduced=30
- AIncidentReportArgs_clone; # systemapi # introduced=30
- AIncidentReportArgs_delete; # systemapi # introduced=30
- AIncidentReportArgs_setAll; # systemapi # introduced=30
- AIncidentReportArgs_setPrivacyPolicy; # systemapi # introduced=30
- AIncidentReportArgs_addSection; # systemapi # introduced=30
- AIncidentReportArgs_setReceiverPackage; # systemapi # introduced=30
- AIncidentReportArgs_setReceiverClass; # systemapi # introduced=30
- AIncidentReportArgs_addHeader; # systemapi # introduced=30
- AIncidentReportArgs_takeReport; # systemapi # introduced=30
+ AIncidentReportArgs_init; # systemapi introduced=30
+ AIncidentReportArgs_clone; # systemapi introduced=30
+ AIncidentReportArgs_delete; # systemapi introduced=30
+ AIncidentReportArgs_setAll; # systemapi introduced=30
+ AIncidentReportArgs_setPrivacyPolicy; # systemapi introduced=30
+ AIncidentReportArgs_addSection; # systemapi introduced=30
+ AIncidentReportArgs_setReceiverPackage; # systemapi introduced=30
+ AIncidentReportArgs_setReceiverClass; # systemapi introduced=30
+ AIncidentReportArgs_addHeader; # systemapi introduced=30
+ AIncidentReportArgs_takeReport; # systemapi introduced=30
local:
*;
};
diff --git a/location/java/android/location/LocationResult.java b/location/java/android/location/LocationResult.java
index 8423000..67f4775 100644
--- a/location/java/android/location/LocationResult.java
+++ b/location/java/android/location/LocationResult.java
@@ -19,8 +19,11 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.location.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -37,6 +40,23 @@
* @hide
*/
public final class LocationResult implements Parcelable {
+ private static final String TAG = "LocationResult";
+
+ // maximum reasonable accuracy, somewhat arbitrarily chosen. this is a very high upper limit, it
+ // could likely be lower, but we only want to throw out really absurd values.
+ private static final float MAX_ACCURACY_M = 1000000;
+
+ // maximum reasonable speed we expect a device to travel at is currently mach 1 (top speed of
+ // current fastest private jet). Higher speed than the value is considered as a malfunction
+ // than a correct reading.
+ private static final float MAX_SPEED_MPS = 343;
+
+ /** Exception representing an invalid location within a {@link LocationResult}. */
+ public static class BadLocationException extends Exception {
+ public BadLocationException(String message) {
+ super(message);
+ }
+ }
/**
* Creates a new LocationResult from the given locations, making a copy of each location.
@@ -101,18 +121,60 @@
*
* @hide
*/
- public @NonNull LocationResult validate() {
+ public @NonNull LocationResult validate() throws BadLocationException {
long prevElapsedRealtimeNs = 0;
final int size = mLocations.size();
for (int i = 0; i < size; ++i) {
Location location = mLocations.get(i);
- if (!location.isComplete()) {
- throw new IllegalArgumentException(
- "incomplete location at index " + i + ": " + mLocations);
- }
- if (location.getElapsedRealtimeNanos() < prevElapsedRealtimeNs) {
- throw new IllegalArgumentException(
- "incorrectly ordered location at index " + i + ": " + mLocations);
+ if (Flags.locationValidation()) {
+ if (location.getLatitude() < -90.0
+ || location.getLatitude() > 90.0
+ || location.getLongitude() < -180.0
+ || location.getLongitude() > 180.0
+ || Double.isNaN(location.getLatitude())
+ || Double.isNaN(location.getLongitude())) {
+ throw new BadLocationException("location must have valid lat/lng");
+ }
+ if (!location.hasAccuracy()) {
+ throw new BadLocationException("location must have accuracy");
+ }
+ if (location.getAccuracy() < 0 || location.getAccuracy() > MAX_ACCURACY_M) {
+ throw new BadLocationException("location must have reasonable accuracy");
+ }
+ if (location.getTime() < 0) {
+ throw new BadLocationException("location must have valid time");
+ }
+ if (prevElapsedRealtimeNs > location.getElapsedRealtimeNanos()) {
+ throw new BadLocationException(
+ "location must have valid monotonically increasing realtime");
+ }
+ if (location.getElapsedRealtimeNanos()
+ > SystemClock.elapsedRealtimeNanos()) {
+ throw new BadLocationException("location must not have realtime in the future");
+ }
+ if (!location.isMock()) {
+ if (location.getProvider() == null) {
+ throw new BadLocationException("location must have valid provider");
+ }
+ if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+ throw new BadLocationException("location must not be at 0,0");
+ }
+ }
+
+ if (location.hasSpeed() && (location.getSpeed() < 0
+ || location.getSpeed() > MAX_SPEED_MPS)) {
+ Log.w(TAG, "removed bad location speed: " + location.getSpeed());
+ location.removeSpeed();
+ }
+ } else {
+ if (!location.isComplete()) {
+ throw new IllegalArgumentException(
+ "incomplete location at index " + i + ": " + mLocations);
+ }
+ if (location.getElapsedRealtimeNanos() < prevElapsedRealtimeNs) {
+ throw new IllegalArgumentException(
+ "incorrectly ordered location at index " + i + ": " + mLocations);
+ }
}
prevElapsedRealtimeNs = location.getElapsedRealtimeNanos();
}
diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/gnss.aconfig
index b6055e8..a8464d3 100644
--- a/location/java/android/location/flags/gnss.aconfig
+++ b/location/java/android/location/flags/gnss.aconfig
@@ -20,3 +20,17 @@
description: "Flag for GnssMeasurementRequest WorkSource API"
bug: "295235160"
}
+
+flag {
+ name: "release_supl_connection_on_timeout"
+ namespace: "location"
+ description: "Flag for releasing SUPL connection on timeout"
+ bug: "315024652"
+}
+
+flag {
+ name: "location_validation"
+ namespace: "location"
+ description: "Flag for location validation"
+ bug: "314328533"
+}
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 8f5f1f6..4fbe9ee 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -48,9 +48,7 @@
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
- }
- ],
- "postsubmit": [
+ },
{
"file_patterns": [
"[^/]*(LoudnessCodec)[^/]*\\.java"
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 5a72b0b..1a3d7b7 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -23,6 +23,8 @@
import android.annotation.TestApi;
import android.util.SparseIntArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -402,7 +404,9 @@
private final AudioDevicePort mPort;
- AudioDeviceInfo(AudioDevicePort port) {
+ /** @hide */
+ @VisibleForTesting
+ public AudioDeviceInfo(AudioDevicePort port) {
mPort = port;
}
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 73bc6f9..2de8eef 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -20,6 +20,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import com.android.aconfig.annotations.VisibleForTesting;
+
import java.util.Arrays;
import java.util.List;
@@ -38,6 +40,26 @@
public class AudioDevicePort extends AudioPort {
+ /** @hide */
+ // TODO: b/316864909 - Remove this method once there's a way to fake audio device ports further
+ // down the stack.
+ @VisibleForTesting
+ public static AudioDevicePort createForTesting(
+ int type, @NonNull String name, @NonNull String address) {
+ return new AudioDevicePort(
+ new AudioHandle(/* id= */ 0),
+ name,
+ /* samplingRates= */ null,
+ /* channelMasks= */ null,
+ /* channelIndexMasks= */ null,
+ /* formats= */ null,
+ /* gains= */ null,
+ type,
+ address,
+ /* encapsulationModes= */ null,
+ /* encapsulationMetadataTypes= */ null);
+ }
+
private final int mType;
private final String mAddress;
private final int[] mEncapsulationModes;
diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java
index 0f48abeb..25b1404 100644
--- a/media/java/android/media/AudioHalVersionInfo.java
+++ b/media/java/android/media/AudioHalVersionInfo.java
@@ -78,11 +78,10 @@
/**
* List of all valid Audio HAL versions. This list need to be in sync with sAudioHALVersions
- * defined in frameworks/av/media/libaudiohal/FactoryHalHidl.cpp.
+ * defined in frameworks/av/media/libaudiohal/FactoryHal.cpp.
*/
- // TODO: add AIDL_1_0 with sAudioHALVersions.
public static final @NonNull List<AudioHalVersionInfo> VERSIONS =
- List.of(HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
+ List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0, HIDL_4_0);
private static final String TAG = "AudioHalVersionInfo";
private AudioHalVersion mHalVersion = new AudioHalVersion();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3dfd572..a5a69f9 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -22,6 +22,7 @@
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
+import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -5120,6 +5121,71 @@
}
/**
+ * Notifies an application with a focus listener of gain or loss of audio focus
+ *
+ * <p>This is similar to {@link #dispatchAudioFocusChange(AudioFocusInfo, int, AudioPolicy)} but
+ * with additional functionality of fade. The players of the application with audio focus
+ * change, provided they meet the active {@link FadeManagerConfiguration} requirements, are
+ * faded before dispatching the callback to the application. For example, players of the
+ * application losing audio focus will be faded out, whereas players of the application gaining
+ * audio focus will be faded in, if needed.
+ *
+ * <p>The applicability of fade is decided against the supplied active {@link AudioFocusInfo}.
+ * This list cannot be {@code null}. The list can be empty if no other active
+ * {@link AudioFocusInfo} available at the time of the dispatch.
+ *
+ * <p>The {@link FadeManagerConfiguration} supplied here is prioritized over existing fade
+ * configurations. If none supplied, either the {@link FadeManagerConfiguration} set through
+ * {@link AudioPolicy} or the default will be used to determine the fade properties.
+ *
+ * <p>This method can only be used by owners of an {@link AudioPolicy} configured with
+ * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true.
+ *
+ * @param afi the recipient of the focus change, that has previously requested audio focus, and
+ * that was received by the {@code AudioPolicy} through
+ * {@link AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}
+ * @param focusChange one of focus gain types ({@link #AUDIOFOCUS_GAIN},
+ * {@link #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or
+ * {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE})
+ * or one of the focus loss types ({@link AudioManager#AUDIOFOCUS_LOSS},
+ * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT},
+ * or {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
+ * <br>For the focus gain, the change type should be the same as the app requested
+ * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
+ * @param otherActiveAfis active {@link AudioFocusInfo} that are granted audio focus at the time
+ * of dispatch
+ * @param transientFadeMgrConfig {@link FadeManagerConfiguration} that will be used for fading
+ * players resulting from this dispatch. This is a transient configuration that is only
+ * valid for this focus change and shall be discarded after processing this request.
+ * @return {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener or if
+ * there was an error sending the request, or {@link #AUDIOFOCUS_REQUEST_GRANTED} if the
+ * dispatch was successfully sent, or {@link #AUDIOFOCUS_REQUEST_DELAYED} if
+ * the request was successful but the dispatch of focus change was delayed due to a fade
+ * operation.
+ * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} or list of
+ * other active {@link AudioFocusInfo} are {@code null}.
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public int dispatchAudioFocusChangeWithFade(@NonNull AudioFocusInfo afi, int focusChange,
+ @NonNull AudioPolicy ap, @NonNull List<AudioFocusInfo> otherActiveAfis,
+ @Nullable FadeManagerConfiguration transientFadeMgrConfig) {
+ Objects.requireNonNull(afi, "AudioFocusInfo cannot be null");
+ Objects.requireNonNull(ap, "AudioPolicy cannot be null");
+ Objects.requireNonNull(otherActiveAfis, "Other active AudioFocusInfo list cannot be null");
+
+ IAudioService service = getService();
+ try {
+ return service.dispatchFocusChangeWithFade(afi, focusChange, ap.cb(), otherActiveAfis,
+ transientFadeMgrConfig);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @hide
* Used internally by telephony package to abandon audio focus, typically after a call or
* when ringing ends and the call is rejected or not answered.
diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java
index 337d4b0..40b0e3e 100644
--- a/media/java/android/media/FadeManagerConfiguration.java
+++ b/media/java/android/media/FadeManagerConfiguration.java
@@ -16,12 +16,13 @@
package android.media;
-import static com.android.media.flags.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
+import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -93,11 +94,9 @@
* Helps with recreating a new instance from another to simply change/add on top of the
* existing ones</li>
* </ul>
- * TODO(b/304835727): Convert into system API so that it can be set through AudioPolicy
- *
* @hide
*/
-
+@SystemApi
@FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
public final class FadeManagerConfiguration implements Parcelable {
@@ -523,6 +522,7 @@
*
* @param fadeState one of the fade state in {@link FadeStateEnum}
* @return human-readable string
+ * @hide
*/
@NonNull
public static String fadeStateToString(@FadeStateEnum int fadeState) {
@@ -712,7 +712,8 @@
*
* <p><b>Notes:</b>
* <ul>
- * <li>When fade state is set to enabled, the builder expects at least one valid usage to be
+ * <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT} or
+ * {@link #FADE_STATE_ENABLED_AUTO}, the builder expects at least one valid usage to be
* set/added. Failure to do so will result in an exception during {@link #build()}</li>
* <li>Every usage added to the fadeable list should have corresponding volume shaper
* configs defined. This can be achieved by setting either the duration or volume shaper
@@ -720,8 +721,8 @@
* {@link #setFadeOutVolumeShaperConfigForUsage(int, VolumeShaper.Configuration)}</li>
* <li> It is recommended to set volume shaper configurations individually for fade out and
* fade in</li>
- * <li>For any incomplete volume shaper configs a volume shaper configuration will be
- * created using either the default fade durations or the ones provided as part of the
+ * <li>For any incomplete volume shaper configurations, a volume shaper configuration will
+ * be created using either the default fade durations or the ones provided as part of the
* {@link #Builder(long, long)}</li>
* <li>Additional volume shaper configs can also configured for a given usage
* with additional attributes like content-type in order to achieve finer fade controls.
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index a52f0b0..5c268d4 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -28,6 +28,7 @@
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.BluetoothProfileConnectionInfo;
+import android.media.FadeManagerConfiguration;
import android.media.IAudioDeviceVolumeDispatcher;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
@@ -398,6 +399,14 @@
int dispatchFocusChange(in AudioFocusInfo afi, in int focusChange,
in IAudioPolicyCallback pcb);
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
+ int dispatchFocusChangeWithFade(in AudioFocusInfo afi,
+ in int focusChange,
+ in IAudioPolicyCallback pcb,
+ in List<AudioFocusInfo> otherActiveAfis,
+ in FadeManagerConfiguration transientFadeMgrConfig);
+
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
@EnforcePermission("BLUETOOTH_STACK")
@@ -754,4 +763,16 @@
oneway void removeLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);
PersistableBundle getLoudnessParams(int piid, in LoudnessCodecInfo codecInfo);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
+ int setFadeManagerConfigurationForFocusLoss(in FadeManagerConfiguration fmcForFocusLoss);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
+ int clearFadeManagerConfigurationForFocusLoss();
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)")
+ FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss();
}
diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index 29bfd1a..e2dddad 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -19,6 +19,7 @@
import android.media.MediaRoute2Info;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
+import android.os.UserHandle;
/**
* @hide
@@ -35,5 +36,6 @@
* Call MediaRouterService#requestCreateSessionWithRouter2 to pass the result.
*/
void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession,
- in MediaRoute2Info route);
+ in MediaRoute2Info route, in UserHandle transferInitiatorUserHandle,
+ in String transferInitiatorPackageName);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index fa4d1a1..04e99ea 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -64,7 +64,8 @@
void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId,
in RoutingSessionInfo oldSession, in MediaRoute2Info route,
- in @nullable Bundle sessionHints);
+ in @nullable Bundle sessionHints, in UserHandle transferInitiatorUserHandle,
+ in String transferInitiatorPackageName);
void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
@@ -84,13 +85,16 @@
void stopScan(IMediaRouter2Manager manager);
void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
- in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
+ in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route,
+ in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName);
void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, in MediaRoute2Info route);
void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, in MediaRoute2Info route);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)")
void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId,
- String sessionId, in MediaRoute2Info route);
+ String sessionId, in MediaRoute2Info route,
+ in UserHandle transferInitiatorUserHandle, String transferInitiatorPackageName);
void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, int volume);
void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 8ad3587..0eabe66 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -19,6 +19,7 @@
import static android.media.MediaRouter2Utils.toUniqueId;
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
import android.annotation.FlaggedApi;
@@ -479,6 +480,37 @@
public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
"android.media.route.feature.REMOTE_GROUP_PLAYBACK";
+ /** Indicates the route is always suitable for media playback. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER = 0;
+
+ /**
+ * Indicates that the route is suitable for media playback only after explicit user selection.
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER = 1;
+
+ /** Indicates that the route is never suitable for media playback. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER = 2;
+
+ /**
+ * Route suitability status.
+ *
+ * <p>Signals whether the route is suitable to play media.
+ *
+ * @hide
+ */
+ @IntDef(
+ value = {
+ SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER,
+ SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER,
+ SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public @interface SuitabilityStatus {}
+
private final String mId;
private final CharSequence mName;
private final List<String> mFeatures;
@@ -500,6 +532,7 @@
private final String mProviderId;
private final boolean mIsVisibilityRestricted;
private final Set<String> mAllowedPackages;
+ @SuitabilityStatus private final int mSuitabilityStatus;
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
@@ -521,6 +554,7 @@
mProviderId = builder.mProviderId;
mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
mAllowedPackages = builder.mAllowedPackages;
+ mSuitabilityStatus = builder.mSuitabilityStatus;
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -544,6 +578,7 @@
mProviderId = in.readString();
mIsVisibilityRestricted = in.readBoolean();
mAllowedPackages = Set.of(in.createString8Array());
+ mSuitabilityStatus = in.readInt();
}
/**
@@ -778,6 +813,13 @@
|| mAllowedPackages.contains(packageName);
}
+ /** Returns the route suitability status. */
+ @SuitabilityStatus
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public int getSuitabilityStatus() {
+ return mSuitabilityStatus;
+ }
+
/**
* Dumps the current state of the object to the given {@code pw} as a human-readable string.
*
@@ -809,6 +851,7 @@
pw.println(indent + "mProviderId=" + mProviderId);
pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
+ pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus);
}
private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -861,39 +904,74 @@
&& Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
&& Objects.equals(mProviderId, other.mProviderId)
&& (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
- && Objects.equals(mAllowedPackages, other.mAllowedPackages);
+ && Objects.equals(mAllowedPackages, other.mAllowedPackages)
+ && mSuitabilityStatus == other.mSuitabilityStatus;
}
@Override
public int hashCode() {
// Note: mExtras is not included.
- return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
- mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
- mVolume, mAddress, mDeduplicationIds, mProviderId, mIsVisibilityRestricted,
- mAllowedPackages);
+ return Objects.hash(
+ mId,
+ mName,
+ mFeatures,
+ mType,
+ mIsSystem,
+ mIconUri,
+ mDescription,
+ mConnectionState,
+ mClientPackageName,
+ mPackageName,
+ mVolumeHandling,
+ mVolumeMax,
+ mVolume,
+ mAddress,
+ mDeduplicationIds,
+ mProviderId,
+ mIsVisibilityRestricted,
+ mAllowedPackages,
+ mSuitabilityStatus);
}
@Override
public String toString() {
// Note: mExtras is not printed here.
- StringBuilder result = new StringBuilder()
- .append("MediaRoute2Info{ ")
- .append("id=").append(getId())
- .append(", name=").append(getName())
- .append(", features=").append(getFeatures())
- .append(", iconUri=").append(getIconUri())
- .append(", description=").append(getDescription())
- .append(", connectionState=").append(getConnectionState())
- .append(", clientPackageName=").append(getClientPackageName())
- .append(", volumeHandling=").append(getVolumeHandling())
- .append(", volumeMax=").append(getVolumeMax())
- .append(", volume=").append(getVolume())
- .append(", address=").append(getAddress())
- .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
- .append(", providerId=").append(getProviderId())
- .append(", isVisibilityRestricted=").append(mIsVisibilityRestricted)
- .append(", allowedPackages=").append(String.join(",", mAllowedPackages))
- .append(" }");
+ StringBuilder result =
+ new StringBuilder()
+ .append("MediaRoute2Info{ ")
+ .append("id=")
+ .append(getId())
+ .append(", name=")
+ .append(getName())
+ .append(", features=")
+ .append(getFeatures())
+ .append(", iconUri=")
+ .append(getIconUri())
+ .append(", description=")
+ .append(getDescription())
+ .append(", connectionState=")
+ .append(getConnectionState())
+ .append(", clientPackageName=")
+ .append(getClientPackageName())
+ .append(", volumeHandling=")
+ .append(getVolumeHandling())
+ .append(", volumeMax=")
+ .append(getVolumeMax())
+ .append(", volume=")
+ .append(getVolume())
+ .append(", address=")
+ .append(getAddress())
+ .append(", deduplicationIds=")
+ .append(String.join(",", getDeduplicationIds()))
+ .append(", providerId=")
+ .append(getProviderId())
+ .append(", isVisibilityRestricted=")
+ .append(mIsVisibilityRestricted)
+ .append(", allowedPackages=")
+ .append(String.join(",", mAllowedPackages))
+ .append(", suitabilityStatus=")
+ .append(mSuitabilityStatus)
+ .append(" }");
return result.toString();
}
@@ -923,6 +1001,7 @@
dest.writeString(mProviderId);
dest.writeBoolean(mIsVisibilityRestricted);
dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
+ dest.writeInt(mSuitabilityStatus);
}
private static String getDeviceTypeString(@Type int deviceType) {
@@ -1005,6 +1084,7 @@
private String mProviderId;
private boolean mIsVisibilityRestricted;
private Set<String> mAllowedPackages;
+ @SuitabilityStatus private int mSuitabilityStatus;
/**
* Constructor for builder to create {@link MediaRoute2Info}.
@@ -1028,6 +1108,7 @@
mFeatures = new ArrayList<>();
mDeduplicationIds = Set.of();
mAllowedPackages = Set.of();
+ mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
}
/**
@@ -1075,6 +1156,7 @@
mProviderId = routeInfo.mProviderId;
mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
mAllowedPackages = routeInfo.mAllowedPackages;
+ mSuitabilityStatus = routeInfo.mSuitabilityStatus;
}
/**
@@ -1318,6 +1400,23 @@
}
/**
+ * Sets route suitability status.
+ *
+ * <p>The default value is {@link
+ * MediaRoute2Info#SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER}.
+ *
+ * <p> Apps are not supposed to set {@link
+ * MediaRoute2Info#SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER}. Publishing a non-system
+ * route with such status throws {@link SecurityException}.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public Builder setSuitabilityStatus(@SuitabilityStatus int suitabilityStatus) {
+ mSuitabilityStatus = suitabilityStatus;
+ return this;
+ }
+
+ /**
* Builds the {@link MediaRoute2Info media route info}.
*
* @throws IllegalArgumentException if no features are added.
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index ba26df9..89792c7 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -17,8 +17,9 @@
package android.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2;
+import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -343,25 +344,13 @@
mImpl = new LocalMediaRouter2Impl(mContext.getPackageName());
mHandler = new Handler(Looper.getMainLooper());
- List<MediaRoute2Info> currentSystemRoutes = null;
- try {
- currentSystemRoutes = mMediaRouterService.getSystemRoutes();
- } catch (RemoteException ex) {
- ex.rethrowFromSystemServer();
- }
-
- if (currentSystemRoutes == null || currentSystemRoutes.isEmpty()) {
- throw new RuntimeException("Null or empty currentSystemRoutes. Something is wrong.");
- }
+ loadSystemRoutes();
RoutingSessionInfo currentSystemSessionInfo = mImpl.getSystemSessionInfo();
if (currentSystemSessionInfo == null) {
throw new RuntimeException("Null currentSystemSessionInfo. Something is wrong.");
}
- for (MediaRoute2Info route : currentSystemRoutes) {
- mRoutes.put(route.getId(), route);
- }
mSystemController = new SystemRoutingController(currentSystemSessionInfo);
}
@@ -373,6 +362,8 @@
IMediaRouterService.Stub.asInterface(
ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+ loadSystemRoutes();
+
mSystemController =
new SystemRoutingController(
ProxyMediaRouter2Impl.getSystemSessionInfoImpl(
@@ -380,6 +371,24 @@
mImpl = new ProxyMediaRouter2Impl(context, clientPackageName, user);
}
+ @GuardedBy("mLock")
+ private void loadSystemRoutes() {
+ List<MediaRoute2Info> currentSystemRoutes = null;
+ try {
+ currentSystemRoutes = mMediaRouterService.getSystemRoutes();
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+
+ if (currentSystemRoutes == null || currentSystemRoutes.isEmpty()) {
+ throw new RuntimeException("Null or empty currentSystemRoutes. Something is wrong.");
+ }
+
+ for (MediaRoute2Info route : currentSystemRoutes) {
+ mRoutes.put(route.getId(), route);
+ }
+ }
+
/**
* Gets the client package name of the app which this media router controls.
*
@@ -699,15 +708,48 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) {
- mImpl.transfer(controller.getRoutingSessionInfo(), route);
+ mImpl.transfer(
+ controller.getRoutingSessionInfo(),
+ route,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
+ }
+
+ /**
+ * Transfers the media of a routing controller to the given route.
+ *
+ * <p>This will be no-op for non-system media routers.
+ *
+ * @param controller a routing controller controlling media routing.
+ * @param route the route you want to transfer the media to.
+ * @param transferInitiatorUserHandle the user handle of the app that initiated the transfer
+ * request.
+ * @param transferInitiatorPackageName the package name of the app that initiated the transfer.
+ * This value is used with the user handle to populate {@link
+ * RoutingController#wasTransferRequestedBySelf()}.
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public void transfer(
+ @NonNull RoutingController controller,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
+ mImpl.transfer(
+ controller.getRoutingSessionInfo(),
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
void requestCreateController(
@NonNull RoutingController controller,
@NonNull MediaRoute2Info route,
- long managerRequestId) {
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
final int requestId = mNextRequestId.getAndIncrement();
@@ -736,7 +778,9 @@
managerRequestId,
controller.getRoutingSessionInfo(),
route,
- controllerHints);
+ controllerHints,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
Log.e(TAG, "createControllerForTransfer: "
+ "Failed to request for creating a controller.", ex);
@@ -1053,7 +1097,11 @@
}
void onRequestCreateControllerByManagerOnHandler(
- RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) {
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Log.i(
TAG,
TextUtils.formatSimple(
@@ -1070,7 +1118,8 @@
if (controller == null) {
return;
}
- requestCreateController(controller, route, managerRequestId);
+ requestCreateController(controller, route, managerRequestId, transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
private List<MediaRoute2Info> getSortedRoutes(
@@ -1469,6 +1518,21 @@
}
/**
+ * Returns whether the transfer was requested by the calling app (as determined by comparing
+ * {@link UserHandle} and package name).
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public boolean wasTransferRequestedBySelf() {
+ RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
+
+ UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
+ String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName();
+
+ return Objects.equals(android.os.Process.myUserHandle(), transferInitiatorUserHandle)
+ && Objects.equals(mContext.getPackageName(), transferInitiatorPackageName);
+ }
+
+ /**
* Returns the current {@link RoutingSessionInfo} associated to this controller.
*/
@NonNull
@@ -1980,14 +2044,20 @@
@Override
public void requestCreateSessionByManager(
- long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
+ long managerRequestId,
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ UserHandle transferInitiatorUserHandle,
+ String transferInitiatorPackageName) {
mHandler.sendMessage(
obtainMessage(
MediaRouter2::onRequestCreateControllerByManagerOnHandler,
MediaRouter2.this,
oldSession,
route,
- managerRequestId));
+ managerRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName));
}
}
@@ -2027,7 +2097,11 @@
void stop();
- void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route);
+ void transfer(
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName);
List<RoutingController> getControllers();
@@ -2220,7 +2294,11 @@
List<RoutingSessionInfo> sessionInfos = getRoutingSessions();
RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
- transfer(targetSession, route);
+ transfer(
+ targetSession,
+ route,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
}
@Override
@@ -2243,14 +2321,24 @@
*
* @param sessionInfo The {@link RoutingSessionInfo routing session} to transfer.
* @param route The {@link MediaRoute2Info route} to transfer to.
- * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info)
+ * @param transferInitiatorUserHandle The user handle of the app that initiated the
+ * transfer.
+ * @param transferInitiatorPackageName The package name if of the app that initiated the
+ * transfer.
+ * @see #transferToRoute(RoutingSessionInfo, MediaRoute2Info, UserHandle, String)
* @see #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)
*/
@Override
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
public void transfer(
- @NonNull RoutingSessionInfo sessionInfo, @NonNull MediaRoute2Info route) {
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
+ Objects.requireNonNull(transferInitiatorPackageName);
Log.v(
TAG,
@@ -2268,9 +2356,14 @@
}
if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
- transferToRoute(sessionInfo, route);
+ transferToRoute(
+ sessionInfo,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} else {
- requestCreateSession(sessionInfo, route);
+ requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
}
@@ -2282,21 +2375,30 @@
* RoutingSessionInfo routing session's} {@link RoutingSessionInfo#getTransferableRoutes()
* transferable routes list}. Otherwise, the request will fail.
*
- * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request
- * an out-of-session transfer.
+ * <p>Use {@link #requestCreateSession(RoutingSessionInfo, MediaRoute2Info)} to request an
+ * out-of-session transfer.
*
* @param session The {@link RoutingSessionInfo routing session} to transfer.
* @param route The {@link MediaRoute2Info route} to transfer to. Must be one of the {@link
* RoutingSessionInfo routing session's} {@link
* RoutingSessionInfo#getTransferableRoutes() transferable routes}.
*/
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
private void transferToRoute(
- @NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) {
+ @NonNull RoutingSessionInfo session,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
int requestId = createTransferRequest(session, route);
try {
mMediaRouterService.transferToRouteWithManager(
- mClient, requestId, session.getId(), route);
+ mClient,
+ requestId,
+ session.getId(),
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -2317,7 +2419,10 @@
* @param route The {@link MediaRoute2Info route} to transfer to.
*/
private void requestCreateSession(
- @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
this.onTransferFailed(oldSession, route);
@@ -2328,7 +2433,12 @@
try {
mMediaRouterService.requestCreateSessionWithManager(
- mClient, requestId, oldSession, route);
+ mClient,
+ requestId,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -3055,7 +3165,8 @@
return;
}
- requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
+ requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE,
+ android.os.Process.myUserHandle(), mContext.getPackageName());
}
@Override
@@ -3071,7 +3182,11 @@
* #transferTo(MediaRoute2Info)}.
*/
@Override
- public void transfer(RoutingSessionInfo sessionInfo, MediaRoute2Info route) {
+ public void transfer(
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
// Do nothing.
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 830708c..06c0996 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -18,9 +18,11 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
@@ -28,6 +30,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -467,30 +470,42 @@
* <p>Same as {@link #transfer(RoutingSessionInfo, MediaRoute2Info)}, but resolves the routing
* session based on the provided package name.
*/
- public void transfer(@NonNull String packageName, @NonNull MediaRoute2Info route) {
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ public void transfer(
+ @NonNull String packageName,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle userHandle) {
Objects.requireNonNull(packageName, "packageName must not be null");
Objects.requireNonNull(route, "route must not be null");
List<RoutingSessionInfo> sessionInfos = getRoutingSessions(packageName);
RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
- transfer(targetSession, route);
+ transfer(targetSession, route, userHandle, packageName);
}
/**
* Transfers a routing session to a media route.
+ *
* <p>{@link Callback#onTransferred} or {@link Callback#onTransferFailed} will be called
* depending on the result.
*
* @param sessionInfo the routing session info to transfer
* @param route the route transfer to
- *
+ * @param transferInitiatorUserHandle the user handle of an app initiated the transfer
+ * @param transferInitiatorPackageName the package name of an app initiated the transfer
* @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
* @see Callback#onTransferFailed(RoutingSessionInfo, MediaRoute2Info)
*/
- public void transfer(@NonNull RoutingSessionInfo sessionInfo,
- @NonNull MediaRoute2Info route) {
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ public void transfer(
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
+ Objects.requireNonNull(transferInitiatorPackageName);
Log.v(TAG, "Transferring routing session. session= " + sessionInfo + ", route=" + route);
@@ -503,9 +518,11 @@
}
if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
- transferToRoute(sessionInfo, route);
+ transferToRoute(
+ sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName);
} else {
- requestCreateSession(sessionInfo, route);
+ requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
}
@@ -873,19 +890,30 @@
*
* @hide
*/
- private void transferToRoute(@NonNull RoutingSessionInfo session,
- @NonNull MediaRoute2Info route) {
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ private void transferToRoute(
+ @NonNull RoutingSessionInfo session,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
int requestId = createTransferRequest(session, route);
try {
mMediaRouterService.transferToRouteWithManager(
- mClient, requestId, session.getId(), route);
+ mClient,
+ requestId,
+ session.getId(),
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
- private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) {
+ private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiationPackageName) {
if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
notifyTransferFailed(oldSession, route);
@@ -896,7 +924,8 @@
try {
mMediaRouterService.requestCreateSessionWithManager(
- mClient, requestId, oldSession, route);
+ mClient, requestId, oldSession, route, transferInitiatorUserHandle,
+ transferInitiationPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index a77c943..d28c26d 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -16,18 +16,25 @@
package android.media;
+import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -55,6 +62,33 @@
private static final String KEY_GROUP_ROUTE = "androidx.mediarouter.media.KEY_GROUP_ROUTE";
private static final String KEY_VOLUME_HANDLING = "volumeHandling";
+ /**
+ * Indicates that the transfer happened by the default logic without explicit system's or user's
+ * request.
+ *
+ * <p>For example, an automatically connected Bluetooth device will have this transfer reason.
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int TRANSFER_REASON_FALLBACK = 0;
+
+ /** Indicates that the transfer happened from within a privileged application. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int TRANSFER_REASON_SYSTEM_REQUEST = 1;
+
+ /** Indicates that the transfer happened from a non-privileged app. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public static final int TRANSFER_REASON_APP = 2;
+
+ /**
+ * Indicates the transfer reason.
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @IntDef(value = {TRANSFER_REASON_FALLBACK, TRANSFER_REASON_SYSTEM_REQUEST, TRANSFER_REASON_APP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransferReason {}
+
@NonNull
final String mId;
@Nullable
@@ -82,6 +116,10 @@
final Bundle mControlHints;
final boolean mIsSystemSession;
+ @TransferReason final int mTransferReason;
+
+ @Nullable final UserHandle mTransferInitiatorUserHandle;
+ @Nullable final String mTransferInitiatorPackageName;
RoutingSessionInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
@@ -116,6 +154,9 @@
volumeAdjustmentForRemoteGroupSessions);
mControlHints = updateVolumeHandlingInHints(builder.mControlHints, mVolumeHandling);
+ mTransferReason = builder.mTransferReason;
+ mTransferInitiatorUserHandle = builder.mTransferInitiatorUserHandle;
+ mTransferInitiatorPackageName = builder.mTransferInitiatorPackageName;
}
RoutingSessionInfo(@NonNull Parcel src) {
@@ -140,6 +181,9 @@
mControlHints = src.readBundle();
mIsSystemSession = src.readBoolean();
+ mTransferReason = src.readInt();
+ mTransferInitiatorUserHandle = src.readParcelable(null, android.os.UserHandle.class);
+ mTransferInitiatorPackageName = src.readString();
}
@Nullable
@@ -330,6 +374,27 @@
return mIsSystemSession;
}
+ /** Returns the transfer reason for this routing session. */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @TransferReason
+ public int getTransferReason() {
+ return mTransferReason;
+ }
+
+ /** @hide */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @Nullable
+ public UserHandle getTransferInitiatorUserHandle() {
+ return mTransferInitiatorUserHandle;
+ }
+
+ /** @hide */
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ @Nullable
+ public String getTransferInitiatorPackageName() {
+ return mTransferInitiatorPackageName;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -351,6 +416,13 @@
dest.writeInt(mVolume);
dest.writeBundle(mControlHints);
dest.writeBoolean(mIsSystemSession);
+ dest.writeInt(mTransferReason);
+ if (mTransferInitiatorUserHandle != null) {
+ mTransferInitiatorUserHandle.writeToParcel(dest, /* flags= */ 0);
+ } else {
+ dest.writeParcelable(null, /* flags= */ 0);
+ }
+ dest.writeString(mTransferInitiatorPackageName);
}
/**
@@ -379,6 +451,9 @@
pw.println(indent + "mVolume=" + mVolume);
pw.println(indent + "mControlHints=" + mControlHints);
pw.println(indent + "mIsSystemSession=" + mIsSystemSession);
+ pw.println(indent + "mTransferReason=" + mTransferReason);
+ pw.println(indent + "mtransferInitiatorUserHandle=" + mTransferInitiatorUserHandle);
+ pw.println(indent + "mtransferInitiatorPackageName=" + mTransferInitiatorPackageName);
}
@Override
@@ -406,39 +481,69 @@
&& Objects.equals(mTransferableRoutes, other.mTransferableRoutes)
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
- && (mVolume == other.mVolume);
+ && (mVolume == other.mVolume)
+ && (mTransferReason == other.mTransferReason)
+ && Objects.equals(mTransferInitiatorUserHandle, other.mTransferInitiatorUserHandle)
+ && Objects.equals(
+ mTransferInitiatorPackageName, other.mTransferInitiatorPackageName);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId,
- mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
- mVolumeMax, mVolumeHandling, mVolume);
+ return Objects.hash(
+ mId,
+ mName,
+ mOwnerPackageName,
+ mClientPackageName,
+ mProviderId,
+ mSelectedRoutes,
+ mSelectableRoutes,
+ mDeselectableRoutes,
+ mTransferableRoutes,
+ mVolumeMax,
+ mVolumeHandling,
+ mVolume,
+ mTransferReason,
+ mTransferInitiatorUserHandle,
+ mTransferInitiatorPackageName);
}
@Override
public String toString() {
- StringBuilder result = new StringBuilder()
- .append("RoutingSessionInfo{ ")
- .append("sessionId=").append(getId())
- .append(", name=").append(getName())
- .append(", clientPackageName=").append(getClientPackageName())
- .append(", selectedRoutes={")
- .append(String.join(",", getSelectedRoutes()))
- .append("}")
- .append(", selectableRoutes={")
- .append(String.join(",", getSelectableRoutes()))
- .append("}")
- .append(", deselectableRoutes={")
- .append(String.join(",", getDeselectableRoutes()))
- .append("}")
- .append(", transferableRoutes={")
- .append(String.join(",", getTransferableRoutes()))
- .append("}")
- .append(", volumeHandling=").append(getVolumeHandling())
- .append(", volumeMax=").append(getVolumeMax())
- .append(", volume=").append(getVolume())
- .append(" }");
+ StringBuilder result =
+ new StringBuilder()
+ .append("RoutingSessionInfo{ ")
+ .append("sessionId=")
+ .append(getId())
+ .append(", name=")
+ .append(getName())
+ .append(", clientPackageName=")
+ .append(getClientPackageName())
+ .append(", selectedRoutes={")
+ .append(String.join(",", getSelectedRoutes()))
+ .append("}")
+ .append(", selectableRoutes={")
+ .append(String.join(",", getSelectableRoutes()))
+ .append("}")
+ .append(", deselectableRoutes={")
+ .append(String.join(",", getDeselectableRoutes()))
+ .append("}")
+ .append(", transferableRoutes={")
+ .append(String.join(",", getTransferableRoutes()))
+ .append("}")
+ .append(", volumeHandling=")
+ .append(getVolumeHandling())
+ .append(", volumeMax=")
+ .append(getVolumeMax())
+ .append(", volume=")
+ .append(getVolume())
+ .append(", transferReason=")
+ .append(getTransferReason())
+ .append(", transferInitiatorUserHandle=")
+ .append(getTransferInitiatorUserHandle())
+ .append(", transferInitiatorPackageName=")
+ .append(getTransferInitiatorPackageName())
+ .append(" }");
return result.toString();
}
@@ -494,6 +599,9 @@
@Nullable
private Bundle mControlHints;
private boolean mIsSystemSession;
+ @TransferReason private int mTransferReason = TRANSFER_REASON_FALLBACK;
+ @Nullable private UserHandle mTransferInitiatorUserHandle;
+ @Nullable private String mTransferInitiatorPackageName;
/**
* Constructor for builder to create {@link RoutingSessionInfo}.
@@ -555,6 +663,9 @@
mControlHints = sessionInfo.mControlHints;
mIsSystemSession = sessionInfo.mIsSystemSession;
+ mTransferReason = sessionInfo.mTransferReason;
+ mTransferInitiatorUserHandle = sessionInfo.mTransferInitiatorUserHandle;
+ mTransferInitiatorPackageName = sessionInfo.mTransferInitiatorPackageName;
}
/**
@@ -784,6 +895,35 @@
}
/**
+ * Sets transfer reason for the current session.
+ *
+ * <p>By default the transfer reason is set to {@link
+ * RoutingSessionInfo#TRANSFER_REASON_FALLBACK}.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public Builder setTransferReason(@TransferReason int transferReason) {
+ mTransferReason = transferReason;
+ return this;
+ }
+
+ /**
+ * Sets the user handle and package name of the process that initiated the transfer.
+ *
+ * <p>By default the transfer initiation user handle and package name are set to {@code
+ * null}.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
+ public Builder setTransferInitiator(
+ @Nullable UserHandle transferInitiatorUserHandle,
+ @Nullable String transferInitiatorPackageName) {
+ mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+ mTransferInitiatorPackageName = transferInitiatorPackageName;
+ return this;
+ }
+
+ /**
* Builds a routing session info.
*
* @throws IllegalArgumentException if no selected routes are added.
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index e168498..b85decc 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -16,6 +16,8 @@
package android.media.audiopolicy;
+import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
+
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -34,6 +36,7 @@
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
+import android.media.FadeManagerConfiguration;
import android.media.IAudioService;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
@@ -49,6 +52,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -612,6 +616,110 @@
return mRegistrationId;
}
+ /**
+ * Sets a custom {@link FadeManagerConfiguration} to handle fade cycle of players during
+ * {@link android.media.AudioManager#AUDIOFOCUS_LOSS}
+ *
+ * @param fmcForFocusLoss custom {@link FadeManagerConfiguration}
+ * @return {@link AudioManager#SUCCESS} if the update was successful,
+ * {@link AudioManager#ERROR} otherwise
+ * @throws IllegalStateException if the audio policy is not registered
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @SystemApi
+ public int setFadeManagerConfigurationForFocusLoss(
+ @NonNull FadeManagerConfiguration fmcForFocusLoss) {
+ Objects.requireNonNull(fmcForFocusLoss,
+ "FadeManagerConfiguration for focus loss cannot be null");
+
+ IAudioService service = getService();
+ synchronized (mLock) {
+ Preconditions.checkState(isAudioPolicyRegisteredLocked(),
+ "Cannot set FadeManagerConfiguration with unregistered AudioPolicy");
+
+ try {
+ return service.setFadeManagerConfigurationForFocusLoss(fmcForFocusLoss);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Received remote exception for setFadeManagerConfigurationForFocusLoss:",
+ e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Clear the current {@link FadeManagerConfiguration} set to handle fade cycles of players
+ * during {@link android.media.AudioManager#AUDIOFOCUS_LOSS}
+ *
+ * <p>In the absence of custom {@link FadeManagerConfiguration}, the default configurations will
+ * be used to handle fade cycles during audio focus loss.
+ *
+ * @return {@link AudioManager#SUCCESS} if the update was successful,
+ * {@link AudioManager#ERROR} otherwise
+ * @throws IllegalStateException if the audio policy is not registered
+ * @see #setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @SystemApi
+ public int clearFadeManagerConfigurationForFocusLoss() {
+ IAudioService service = getService();
+ synchronized (mLock) {
+ Preconditions.checkState(isAudioPolicyRegisteredLocked(),
+ "Cannot clear FadeManagerConfiguration from unregistered AudioPolicy");
+
+ try {
+ return service.clearFadeManagerConfigurationForFocusLoss();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Received remote exception for "
+ + "clearFadeManagerConfigurationForFocusLoss:", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Get the current fade manager configuration used for fade operations during
+ * {@link android.media.AudioManager#AUDIOFOCUS_LOSS}
+ *
+ * <p>If no custom {@link FadeManagerConfiguration} is set, the default configuration currently
+ * active will be returned.
+ *
+ * @return the active {@link FadeManagerConfiguration} used during audio focus loss
+ * @throws IllegalStateException if the audio policy is not registered
+ * @see #setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)
+ * @see #clearFadeManagerConfigurationForFocusLoss()
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @SystemApi
+ @NonNull
+ public FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss() {
+ IAudioService service = getService();
+ synchronized (mLock) {
+ Preconditions.checkState(isAudioPolicyRegisteredLocked(),
+ "Cannot get FadeManagerConfiguration from unregistered AudioPolicy");
+
+ try {
+ return service.getFadeManagerConfigurationForFocusLoss();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Received remote exception for getFadeManagerConfigurationForFocusLoss:",
+ e);
+ throw e.rethrowFromSystemServer();
+
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isAudioPolicyRegisteredLocked() {
+ return mStatus == POLICY_STATUS_REGISTERED;
+ }
+
private boolean policyReadyToUse() {
synchronized (mLock) {
if (mStatus != POLICY_STATUS_REGISTERED) {
diff --git a/media/java/android/media/flags/fade_manager_configuration.aconfig b/media/java/android/media/flags/fade_manager_configuration.aconfig
deleted file mode 100644
index 100e2235..0000000
--- a/media/java/android/media/flags/fade_manager_configuration.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "com.android.media.flags"
-
-flag {
- namespace: "media_solutions"
- name: "enable_fade_manager_configuration"
- description: "Enable Fade Manager Configuration support to determine fade properties"
- bug: "307354764"
-}
\ No newline at end of file
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 07f63e5..7f95886 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -69,3 +69,17 @@
description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
bug: "314324170"
}
+
+flag {
+ name: "enable_built_in_speaker_route_suitability_statuses"
+ namespace: "media_solutions"
+ description: "Make MediaRoute2Info provide information about routes suitability for transfer."
+ bug: "279555229"
+}
+
+flag {
+ name: "enable_notifying_activity_manager_with_media_session_status_change"
+ namespace: "media_solutions"
+ description: "Notify ActivityManager with the changes in playback state of the media session."
+ bug: "295518668"
+}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 7891ee6..60497fe 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -524,6 +524,28 @@
return false;
}
+ /**
+ * Returns whether the service holding the media session should run in the foreground when the
+ * media session has this playback state or not.
+ *
+ * @hide
+ */
+ public boolean shouldAllowServiceToRunInForeground() {
+ switch (mState) {
+ case PlaybackState.STATE_PLAYING:
+ case PlaybackState.STATE_FAST_FORWARDING:
+ case PlaybackState.STATE_REWINDING:
+ case PlaybackState.STATE_BUFFERING:
+ case PlaybackState.STATE_CONNECTING:
+ case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+ case PlaybackState.STATE_SKIPPING_TO_NEXT:
+ case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
+ return true;
+ default:
+ return false;
+ }
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<PlaybackState> CREATOR =
new Parcelable.Creator<PlaybackState>() {
@Override
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 757e9f8..3fcb871 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -693,6 +693,8 @@
mpuSequenceNumber, isPesPrivateData, sc,
audioDescriptor.get(), presentationsJObj.get()));
+ // Protect mFilterClient from being set to null.
+ android::Mutex::Autolock autoLock(mLock);
uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
(dataLength > 0 && (dataLength + offset) < avSharedMemSize)) {
@@ -939,38 +941,52 @@
}
}
}
- ScopedLocalRef filter(env, env->NewLocalRef(mFilterObj));
- if (!env->IsSameObject(filter.get(), nullptr)) {
- jmethodID methodID = gFields.onFilterEventID;
- if (mSharedFilter) {
- methodID = gFields.onSharedFilterEventID;
+
+ ScopedLocalRef<jobject> filter(env);
+ {
+ android::Mutex::Autolock autoLock(mLock);
+ if (env->IsSameObject(mFilterObj, nullptr)) {
+ ALOGE("FilterClientCallbackImpl::onFilterEvent:"
+ "Filter object has been freed. Ignoring callback.");
+ return;
+ } else {
+ filter.reset(env->NewLocalRef(mFilterObj));
}
- env->CallVoidMethod(filter.get(), methodID, array.get());
- } else {
- ALOGE("FilterClientCallbackImpl::onFilterEvent:"
- "Filter object has been freed. Ignoring callback.");
}
+
+ jmethodID methodID = gFields.onFilterEventID;
+ if (mSharedFilter) {
+ methodID = gFields.onSharedFilterEventID;
+ }
+ env->CallVoidMethod(filter.get(), methodID, array.get());
}
void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
ALOGV("FilterClientCallbackImpl::onFilterStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- ScopedLocalRef filter(env, env->NewLocalRef(mFilterObj));
- if (!env->IsSameObject(filter.get(), nullptr)) {
- jmethodID methodID = gFields.onFilterStatusID;
- if (mSharedFilter) {
- methodID = gFields.onSharedFilterStatusID;
+ ScopedLocalRef<jobject> filter(env);
+ {
+ android::Mutex::Autolock autoLock(mLock);
+ if (env->IsSameObject(filter.get(), nullptr)) {
+ ALOGE("FilterClientCallbackImpl::onFilterStatus:"
+ "Filter object has been freed. Ignoring callback.");
+ return;
+ } else {
+ filter.reset(env->NewLocalRef(mFilterObj));
}
- env->CallVoidMethod(filter.get(), methodID, (jint)static_cast<uint8_t>(status));
- } else {
- ALOGE("FilterClientCallbackImpl::onFilterStatus:"
- "Filter object has been freed. Ignoring callback.");
}
+
+ jmethodID methodID = gFields.onFilterStatusID;
+ if (mSharedFilter) {
+ methodID = gFields.onSharedFilterStatusID;
+ }
+ env->CallVoidMethod(filter.get(), methodID, (jint)static_cast<uint8_t>(status));
}
void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) {
ALOGV("FilterClientCallbackImpl::setFilter");
// Java Object
+ android::Mutex::Autolock autoLock(mLock);
mFilterObj = filterObj;
mFilterClient = filterClient;
mSharedFilter = false;
@@ -979,6 +995,7 @@
void FilterClientCallbackImpl::setSharedFilter(jweak filterObj, sp<FilterClient> filterClient) {
ALOGV("FilterClientCallbackImpl::setFilter");
// Java Object
+ android::Mutex::Autolock autoLock(mLock);
mFilterObj = filterObj;
mFilterClient = filterClient;
mSharedFilter = true;
@@ -1047,11 +1064,14 @@
FilterClientCallbackImpl::~FilterClientCallbackImpl() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mFilterObj != nullptr) {
- env->DeleteWeakGlobalRef(mFilterObj);
- mFilterObj = nullptr;
+ {
+ android::Mutex::Autolock autoLock(mLock);
+ if (mFilterObj != nullptr) {
+ env->DeleteWeakGlobalRef(mFilterObj);
+ mFilterObj = nullptr;
+ }
+ mFilterClient = nullptr;
}
- mFilterClient = nullptr;
env->DeleteGlobalRef(mEventClass);
env->DeleteGlobalRef(mSectionEventClass);
env->DeleteGlobalRef(mMediaEventClass);
@@ -3696,7 +3716,7 @@
"([Landroid/media/tv/tuner/filter/FilterEvent;)V");
jclass sharedFilterClazz = env->FindClass("android/media/tv/tuner/filter/SharedFilter");
- gFields.sharedFilterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
+ gFields.sharedFilterContext = env->GetFieldID(sharedFilterClazz, "mNativeContext", "J");
gFields.sharedFilterInitID = env->GetMethodID(sharedFilterClazz, "<init>", "()V");
gFields.onSharedFilterStatusID = env->GetMethodID(sharedFilterClazz, "onFilterStatus", "(I)V");
gFields.onSharedFilterEventID =
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 01c998d..3de3ab9 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -136,6 +136,7 @@
private:
jweak mFilterObj;
sp<FilterClient> mFilterClient;
+ android::Mutex mLock;
jclass mEventClass;
jclass mSectionEventClass;
jclass mMediaEventClass;
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
index fb6bd48..f105ae9 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
@@ -16,7 +16,7 @@
package com.android.audiopolicytest;
-import static com.android.media.flags.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
+import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
import static org.junit.Assert.assertThrows;
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index 3b15632..ce1004c 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -95,12 +95,17 @@
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void setAudioTrack_callsAudioServiceStart() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
- anyList());
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+ anyList());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -108,10 +113,15 @@
public void getLoudnessCodecParams_callsAudioServiceGetLoudness() throws Exception {
when(mAudioService.getLoudnessParams(anyInt(), any())).thenReturn(new PersistableBundle());
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.getLoudnessCodecParams(track, createAndConfigureMediaCodec());
+ try {
+ mLcc.getLoudnessCodecParams(track, mediaCodec);
- verify(mAudioService).getLoudnessParams(eq(track.getPlayerIId()), any());
+ verify(mAudioService).getLoudnessParams(eq(track.getPlayerIId()), any());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -120,10 +130,14 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -132,24 +146,33 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
+ mLcc.setAudioTrack(track);
- verify(mAudioService, times(1)).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
- anyList());
+ verify(mAudioService, times(1)).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+ anyList());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void setTrackNull_stopCodecUpdates() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
- mLcc.setAudioTrack(null); // stops updates
- verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ mLcc.setAudioTrack(null); // stops updates
+ verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -157,27 +180,37 @@
public void addMediaCodecTwice_triggersIAE() throws Exception {
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
- assertThrows(IllegalArgumentException.class, () -> mLcc.addMediaCodec(mediaCodec));
+ assertThrows(IllegalArgumentException.class, () -> mLcc.addMediaCodec(mediaCodec));
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void setClearTrack_removeAllAudioServicePiidCodecs() throws Exception {
final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
-
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+ final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
- argument.capture());
- assertEquals(argument.getValue().size(), 1);
+ try {
+ mLcc.addMediaCodec(mediaCodec1);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+ argument.capture());
+ assertEquals(argument.getValue().size(), 1);
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(null);
- verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ mLcc.addMediaCodec(mediaCodec2);
+ mLcc.setAudioTrack(null);
+ verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ } finally {
+ mediaCodec1.release();
+ mediaCodec2.release();
+ }
}
@Test
@@ -186,24 +219,35 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
- mLcc.removeMediaCodec(mediaCodec);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
+ mLcc.removeMediaCodec(mediaCodec);
- verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void addMediaCodecAfterSetTrack_callsAudioServiceAdd() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+ final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ try {
+ mLcc.addMediaCodec(mediaCodec1);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
+ mLcc.addMediaCodec(mediaCodec2);
+ verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
+ } finally {
+ mediaCodec1.release();
+ mediaCodec2.release();
+ }
}
@Test
@@ -212,25 +256,36 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
- mLcc.removeMediaCodec(mediaCodec);
- verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ mLcc.removeMediaCodec(mediaCodec);
+ verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void removeWrongMediaCodecAfterSetTrack_triggersIAE() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+ final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ try {
+ mLcc.addMediaCodec(mediaCodec1);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
- assertThrows(IllegalArgumentException.class,
- () -> mLcc.removeMediaCodec(createAndConfigureMediaCodec()));
+ assertThrows(IllegalArgumentException.class,
+ () -> mLcc.removeMediaCodec(mediaCodec2));
+ } finally {
+ mediaCodec1.release();
+ mediaCodec2.release();
+ }
}
private static AudioTrack createAudioTrack() {
@@ -250,19 +305,21 @@
MediaExtractor extractor;
extractor = new MediaExtractor();
- extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ try {
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
testFd.getLength());
- testFd.close();
+ assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
+ MediaFormat format = extractor.getTrackFormat(0);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ assertTrue("not an audio file", mime.startsWith(TEST_MEDIA_AUDIO_CODEC_PREFIX));
+ final MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);
- assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
- MediaFormat format = extractor.getTrackFormat(0);
- String mime = format.getString(MediaFormat.KEY_MIME);
- assertTrue("not an audio file", mime.startsWith(TEST_MEDIA_AUDIO_CODEC_PREFIX));
- final MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);
-
- Log.v(TAG, "configuring with " + format);
- mediaCodec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-
- return mediaCodec;
+ Log.v(TAG, "configuring with " + format);
+ mediaCodec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+ return mediaCodec;
+ } finally {
+ testFd.close();
+ extractor.release();
+ }
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 8ed4bf2..c836df3 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -385,7 +385,9 @@
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
assertThat(routeToSelect).isNotNull();
- mManager.transfer(mPackageName, routeToSelect);
+ mManager.transfer(
+ mPackageName, routeToSelect,
+ android.os.Process.myUserHandle());
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(mManager.getRemoteSessions()).hasSize(1);
}
@@ -411,7 +413,9 @@
assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
- mManager.transfer(mPackageName, routeToSelect);
+ mManager.transfer(
+ mPackageName, routeToSelect,
+ android.os.Process.myUserHandle());
assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -450,7 +454,11 @@
.addFeature(FEATURE_REMOTE_PLAYBACK)
.build();
- mManager.transfer(mManager.getSystemRoutingSession(null), unknownRoute);
+ mManager.transfer(
+ mManager.getSystemRoutingSession(null),
+ unknownRoute,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
assertThat(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
@@ -484,7 +492,11 @@
assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(1);
assertThat(mRouter2.getControllers()).hasSize(1);
- mManager.transfer(mManager.getRoutingSessions(mPackageName).get(0), routeToSelect);
+ mManager.transfer(
+ mManager.getRoutingSessions(mPackageName).get(0),
+ routeToSelect,
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
assertThat(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(mManager.getRoutingSessions(mPackageName)).hasSize(2);
@@ -516,7 +528,11 @@
}
});
awaitOnRouteChangedManager(
- () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID1)),
+ () ->
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID1),
+ android.os.Process.myUserHandle()),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
@@ -527,7 +543,11 @@
RoutingSessionInfo sessionInfo = sessions.get(1);
awaitOnRouteChangedManager(
- () -> mManager.transfer(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
+ () ->
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID5_TO_TRANSFER_TO),
+ android.os.Process.myUserHandle()),
ROUTE_ID5_TO_TRANSFER_TO,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
@@ -585,9 +605,11 @@
assertThat(route1).isNotNull();
assertThat(route2).isNotNull();
- mManager.transfer(mPackageName, route1);
+ mManager.transfer(
+ mPackageName, route1, android.os.Process.myUserHandle());
assertThat(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
- mManager.transfer(mPackageName, route2);
+ mManager.transfer(
+ mPackageName, route2, android.os.Process.myUserHandle());
assertThat(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
// onTransferFailed/onSessionReleased should not be called.
@@ -634,7 +656,11 @@
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
RoutingSessionInfo targetSession = sessions.get(sessions.size() - 1);
- mManager.transfer(targetSession, routes.get(ROUTE_ID6_TO_BE_IGNORED));
+ mManager.transfer(
+ targetSession,
+ routes.get(ROUTE_ID6_TO_BE_IGNORED),
+ android.os.Process.myUserHandle(),
+ mContext.getPackageName());
assertThat(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)).isFalse();
assertThat(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS,
@@ -705,7 +731,10 @@
}
});
- mManager.transfer(mPackageName, routes.get(ROUTE_ID1));
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID1),
+ android.os.Process.myUserHandle());
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
@@ -860,7 +889,8 @@
});
mRouter2.setOnGetControllerHintsListener(listener);
- mManager.transfer(mPackageName, route);
+ mManager.transfer(
+ mPackageName, route, android.os.Process.myUserHandle());
assertThat(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
assertThat(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
@@ -905,7 +935,10 @@
}
});
- mManager.transfer(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT));
+ mManager.transfer(
+ mPackageName,
+ routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT),
+ android.os.Process.myUserHandle());
assertThat(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 9f2a9ac..9605108 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -1,9 +1,9 @@
LIBANDROID {
global:
- AActivityManager_addUidImportanceListener; # systemapi # introduced=31
- AActivityManager_removeUidImportanceListener; # systemapi # introduced=31
- AActivityManager_isUidActive; # systemapi # introduced=31
- AActivityManager_getUidImportance; # systemapi # introduced=31
+ AActivityManager_addUidImportanceListener; # systemapi introduced=31
+ AActivityManager_removeUidImportanceListener; # systemapi introduced=31
+ AActivityManager_isUidActive; # systemapi introduced=31
+ AActivityManager_getUidImportance; # systemapi introduced=31
AAssetDir_close;
AAssetDir_getNextFileName;
AAssetDir_rewind;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c4c8128..abe4a3d 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -18,10 +18,12 @@
#include <aidl/android/hardware/power/SessionHint.h>
#include <aidl/android/hardware/power/SessionMode.h>
+#include <android-base/stringprintf.h>
#include <android/WorkDuration.h>
#include <android/os/IHintManager.h>
#include <android/os/IHintSession.h>
#include <android/performance_hint.h>
+#include <android/trace.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
@@ -30,6 +32,7 @@
#include <utils/SystemClock.h>
#include <chrono>
+#include <set>
#include <utility>
#include <vector>
@@ -40,6 +43,7 @@
using AidlSessionHint = aidl::android::hardware::power::SessionHint;
using AidlSessionMode = aidl::android::hardware::power::SessionMode;
+using android::base::StringPrintf;
struct APerformanceHintSession;
@@ -98,10 +102,21 @@
std::vector<int64_t> mLastHintSentTimestamp;
// Cached samples
std::vector<WorkDuration> mActualWorkDurations;
+ std::string mSessionName;
+ static int32_t sIDCounter;
+ // The most recent set of thread IDs
+ std::vector<int32_t> mLastThreadIDs;
+ // Tracing helpers
+ void traceThreads(std::vector<int32_t>& tids);
+ void tracePowerEfficient(bool powerEfficient);
+ void traceActualDuration(int64_t actualDuration);
+ void traceBatchSize(size_t batchSize);
+ void traceTargetDuration(int64_t targetDuration);
};
static IHintManager* gIHintManagerForTesting = nullptr;
static APerformanceHintManager* gHintManagerForTesting = nullptr;
+int32_t APerformanceHintSession::sIDCounter = 0;
// ===================================== APerformanceHintManager implementation
APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager,
@@ -150,8 +165,12 @@
if (!ret.isOk() || !session) {
return nullptr;
}
- return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
- initialTargetWorkDurationNanos);
+ auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
+ initialTargetWorkDurationNanos);
+ out->traceThreads(tids);
+ out->traceTargetDuration(initialTargetWorkDurationNanos);
+ out->tracePowerEfficient(false);
+ return out;
}
int64_t APerformanceHintManager::getPreferredRateNanos() const {
@@ -174,6 +193,7 @@
ndk::enum_range<AidlSessionHint>().end()};
mLastHintSentTimestamp = std::vector<int64_t>(sessionHintRange.size(), 0);
+ mSessionName = android::base::StringPrintf("ADPF Session %" PRId32, ++sIDCounter);
}
APerformanceHintSession::~APerformanceHintSession() {
@@ -200,6 +220,8 @@
* as they are most likely obsolete.
*/
mActualWorkDurations.clear();
+ traceBatchSize(0);
+ traceTargetDuration(targetDurationNanos);
mFirstTargetMetTimestamp = 0;
mLastTargetMetTimestamp = 0;
return 0;
@@ -254,6 +276,9 @@
}
return EPIPE;
}
+
+ traceThreads(tids);
+
return 0;
}
@@ -289,6 +314,7 @@
ret.exceptionMessage().c_str());
return EPIPE;
}
+ tracePowerEfficient(enabled);
return OK;
}
@@ -318,6 +344,7 @@
int64_t actualTotalDurationNanos = workDuration->actualTotalDurationNanos;
int64_t now = uptimeNanos();
workDuration->timestampNanos = now;
+ traceActualDuration(workDuration->actualTotalDurationNanos);
mActualWorkDurations.push_back(std::move(*workDuration));
if (actualTotalDurationNanos >= mTargetDurationNanos) {
@@ -335,6 +362,7 @@
*/
if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
+ traceBatchSize(mActualWorkDurations.size());
return 0;
}
mLastTargetMetTimestamp = now;
@@ -346,12 +374,54 @@
ret.exceptionMessage().c_str());
mFirstTargetMetTimestamp = 0;
mLastTargetMetTimestamp = 0;
+ traceBatchSize(mActualWorkDurations.size());
return ret.exceptionCode() == binder::Status::EX_ILLEGAL_ARGUMENT ? EINVAL : EPIPE;
}
mActualWorkDurations.clear();
+ traceBatchSize(0);
return 0;
}
+// ===================================== Tracing helpers
+
+void APerformanceHintSession::traceThreads(std::vector<int32_t>& tids) {
+ std::set<int32_t> tidSet{tids.begin(), tids.end()};
+
+ // Disable old TID tracing
+ for (int32_t tid : mLastThreadIDs) {
+ if (!tidSet.count(tid)) {
+ std::string traceName =
+ android::base::StringPrintf("%s TID: %" PRId32, mSessionName.c_str(), tid);
+ ATrace_setCounter(traceName.c_str(), 0);
+ }
+ }
+
+ // Add new TID tracing
+ for (int32_t tid : tids) {
+ std::string traceName =
+ android::base::StringPrintf("%s TID: %" PRId32, mSessionName.c_str(), tid);
+ ATrace_setCounter(traceName.c_str(), 1);
+ }
+
+ mLastThreadIDs = std::move(tids);
+}
+
+void APerformanceHintSession::tracePowerEfficient(bool powerEfficient) {
+ ATrace_setCounter((mSessionName + " power efficiency mode").c_str(), powerEfficient);
+}
+
+void APerformanceHintSession::traceActualDuration(int64_t actualDuration) {
+ ATrace_setCounter((mSessionName + " actual duration").c_str(), actualDuration);
+}
+
+void APerformanceHintSession::traceBatchSize(size_t batchSize) {
+ std::string traceName = StringPrintf("%s batch size", mSessionName.c_str());
+ ATrace_setCounter((mSessionName + " batch size").c_str(), batchSize);
+}
+
+void APerformanceHintSession::traceTargetDuration(int64_t targetDuration) {
+ ATrace_setCounter((mSessionName + " target duration").c_str(), targetDuration);
+}
// ===================================== C API
APerformanceHintManager* APerformanceHint_getManager() {
diff --git a/packages/CarrierDefaultApp/res/values-fr/strings.xml b/packages/CarrierDefaultApp/res/values-fr/strings.xml
index 4bc03ffa..9e85969 100644
--- a/packages/CarrierDefaultApp/res/values-fr/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-fr/strings.xml
@@ -16,7 +16,7 @@
<string name="ssl_error_continue" msgid="1138548463994095584">"Continuer quand même dans le navigateur"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Boost de performances"</string>
<string name="performance_boost_notification_title" msgid="3126203390685781861">"Options 5G de votre opérateur"</string>
- <string name="performance_boost_notification_detail" msgid="216569851036236346">"Accédez au site Web de %s pour consulter les options pour l\'expérience de votre appli"</string>
+ <string name="performance_boost_notification_detail" msgid="216569851036236346">"Accédez au site Web de %s pour consulter les options de votre appli"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Pas maintenant"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Gérer"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Achetez un boost de performances."</string>
diff --git a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
index db8ebb4..1ac5db6 100644
--- a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
@@ -18,54 +18,63 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- style="@style/ScrollViewStyle">
+ style="@style/ScrollViewStyle"
+ android:importantForAccessibility="no">
<LinearLayout
- android:id="@+id/data_transfer_confirmation"
- style="@style/ContainerLayout">
-
- <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
-
- <ImageView
- android:id="@+id/header_icon"
- android:layout_width="match_parent"
- android:layout_height="32dp"
- android:gravity="center"
- android:layout_marginTop="18dp"
- android:src="@drawable/ic_warning"
- android:contentDescription="@null" />
-
- <LinearLayout style="@style/Description">
-
- <TextView
- android:id="@+id/title"
- style="@style/DescriptionTitle" />
-
- <TextView
- android:id="@+id/summary"
- style="@style/DescriptionSummary" />
-
- </LinearLayout>
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="false"
+ android:importantForAccessibility="no">
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="18dp">
+ android:id="@+id/data_transfer_confirmation"
+ style="@style/ContainerLayout">
- <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+ <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
- <Button
- android:id="@+id/btn_positive"
- style="@style/PositiveButton"
- android:text="@string/consent_yes" />
+ <ImageView
+ android:id="@+id/header_icon"
+ android:layout_width="match_parent"
+ android:layout_height="32dp"
+ android:gravity="center"
+ android:layout_marginTop="18dp"
+ android:src="@drawable/ic_warning"
+ android:contentDescription="@null" />
- <Button
- android:id="@+id/btn_negative"
- style="@style/NegativeButton"
- android:text="@string/consent_no" />
+ <LinearLayout style="@style/Description">
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/DescriptionTitle" />
+
+ <TextView
+ android:id="@+id/summary"
+ style="@style/DescriptionSummary" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="18dp">
+
+ <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+
+ <Button
+ android:id="@+id/btn_positive"
+ style="@style/PositiveButton"
+ android:text="@string/consent_yes" />
+
+ <Button
+ android:id="@+id/btn_negative"
+ style="@style/NegativeButton"
+ android:text="@string/consent_no" />
+
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index 285d2d1..1f39509 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Toestelle in die omtrek"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Verander media-uitset"</string>
<string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Kennisgewings"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Stroming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Maak en bestuur foonoproepe"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lees en skryf foonoproeprekord neer"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Stuur en bekyk SMS-boodskappe"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Verkry toegang tot jou kontakte"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Verkry toegang tot jou kalender"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Neem oudio op"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vind, koppel aan en bepaal die relatiewe posisie van toestelle in die omtrek"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lees alle kennisgewings, insluitend inligting soos kontakte, boodskappe en foto’s"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lees alle kennisgewings, insluitend inligting soos kontakte, boodskappe en foto’s<br/>• Stuur kennisgewings<br/><br/>Jy kan hierdie app se vermoë om kennisgewings te lees en te stuur, enige tyd in Instellings bestuur > Kennisgewings."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Stroom jou foon se apps"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stroom apps en ander stelselkenmerke van jou foon af"</string>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index 996d2ea..007173c 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"በአቅራቢያ ያሉ መሣሪያዎች"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"የሚዲያ ውጤትን ይቀይሩ"</string>
<string name="permission_storage" msgid="6831099350839392343">"ፎቶዎች እና ሚዲያ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"ማሳወቂያዎች"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"መተግበሪያዎች"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"በዥረት መልቀቅ"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"የስልክ ጥሪዎች ያድርጉ እና ያስተዳድሩ"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"የስልክ ጥሪ ምዝግብ ማስታወሻን ያንብቡ እና ይጻፉ"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"የኤስኤምኤስ መልዕክቶችን ይላኩና ይመልከቱ"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"የእርስዎን እውቂያዎች ይድረሱባቸው"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"የእርስዎን ቀን መቁጠሪያ ይድረሱበት"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ኦዲዮ ቅረጽ"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"በአቅራቢያ ያሉ መሣሪያዎችን ማግኘት፣ ከእነሱ ጋር መገናኘት እና አንጻራዊ ቦታቸውን መወሰን"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"እንደ እውቂያዎች፣ መልዕክቶች እና ፎቶዎች ያሉ መረጃዎችን ጨምሮ ሁሉንም ማሳወቂያዎች ማንበብ"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• እንደ እውቂያዎች፣ መልዕክቶች እና ፎቶዎች ያሉ መረጃዎችን ጨምሮ ሁሉንም ማሳወቂያዎች ያንብቡ<br/>• ማሳወቂያዎችን መላክ<br/><br/>የዚህን መተግበሪያ ማሳወቂያዎችን የማንበብ እና የመላክ ችሎታን በቅንብሮች > ማሳወቂያዎች ውስጥ ማስተዳደር ይችላሉ።"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"የስልክዎን መተግበሪያዎች በዥረት ይልቀቁ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ከስልክዎ ሆነው መተግበሪያዎች እና ሌሎች የስርዓት ባህሪያትን በዥረት ይልቀቁ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 58cf926..5b893b0 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -54,8 +54,7 @@
<string name="permission_microphone" msgid="2152206421428732949">"الميكروفون"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"سجلّ المكالمات"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"الأجهزة المجاورة"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"تغيير جهاز إخراج الوسائط"</string>
<string name="permission_storage" msgid="6831099350839392343">"الصور والوسائط"</string>
<!-- no translation found for permission_notifications (4099418516590632909) -->
<skip />
@@ -82,8 +81,7 @@
<string name="permission_app_streaming_summary" msgid="606923325679670624">"بث تطبيقات هاتفك"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"بثّ التطبيقات وميزات النظام الأخرى من هاتفك"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"يتيح لك هذا الإذن الاطّلاع على مجموعة من الأجهزة المتاحة وتحديد الجهاز المسموح له بتشغيل الصوت أو الفيديو أو بثّهما من التطبيقات الأخرى."</string>
<string name="device_type" product="default" msgid="8268703872070046263">"هاتف"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"جهاز لوحي"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index fddc24c..623b212 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"নিকটৱৰ্তী ডিভাইচ"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"মিডিয়া আউটপুট সলনি কৰক"</string>
<string name="permission_storage" msgid="6831099350839392343">"ফট’ আৰু মিডিয়া"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"জাননী"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"এপ্"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ষ্ট্ৰীম কৰি থকা হৈছে"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ফ’ন কল কৰক আৰু সেয়া পৰিচলনা কৰক"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ফ’নৰ কল লগ পঢ়ক আৰু লিখক"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"এছএমএছ বার্তাবোৰ প্ৰেৰণ কৰক আৰু চাওক"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"আপোনাৰ সম্পৰ্কসূচী এক্সেছ কৰক"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"আপোনাৰ কেলেণ্ডাৰ এক্সেছ কৰক"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"অডিঅ’ ৰেকৰ্ড কৰক"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"নিকটৱৰ্তী ডিভাইচসমূহ বিচাৰক, সেইসমূহৰ সৈতে সংযোগ কৰক আৰু সেইসমূহৰ প্ৰাসংগিক অৱস্থান নিৰ্ধাৰণ কৰক"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"সম্পৰ্কসূচী, বাৰ্তা আৰু ফট’ৰ দৰে তথ্যকে ধৰি আটাইবোৰ জাননী পঢ়ক"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• সম্পৰ্কসূচী, বাৰ্তা আৰু ফট’ৰ দৰে তথ্যকে ধৰি আটাইবোৰ জাননী পঢ়ক<br/>• জাননী পঠিয়াওক<br/><br/>আপুনি যিকোনো সময়তে ছেটিং > জাননীলৈ গৈ এই এপ্টোৰ জাননী পঢ়া আৰু পঠিওৱাৰ সক্ষমতা পৰিচালনা কৰিব পাৰে।"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"আপোনাৰ ফ’নৰ এপ্ ষ্ট্ৰীম কৰক"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"আপোনাৰ ফ’নৰ পৰা এপ্ আৰু ছিষ্টেমৰ অন্য সুবিধাসমূহ ষ্ট্ৰীম কৰক"</string>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index a3ed669..65f3a62 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Yaxınlıqdakı cihazlar"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Media çıxışını dəyişin"</string>
<string name="permission_storage" msgid="6831099350839392343">"Foto və media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Bildirişlər"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Tətbiqlər"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Yayım"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon zənglərinin edilməsi və idarəsi"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon zəng siyahısının oxunması və yazılması"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mesajlarının göndərilməsi və onlara baxmaq"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Kontaktlara giriş"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Təqvimə giriş"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Audionun qeydə alınması"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Yaxınlıqdakı cihazların tapılması, onlara qoşulmaq və nisbi mövqelərinin təyin edilməsi"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kontakt, mesaj və foto kimi məlumatlar daxil olmaqla bütün bildirişlərin oxunması"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kontakt, mesaj və foto kimi məlumatlar daxil olmaqla bütün bildirişlərin oxunması<br/>• Bildirişlərin göndərilməsi<br/><br/>Bu tətbiqin bildirişləri oxumaq və göndərmək imkanını Ayarlar > Bildirişlər bölməsində idarə edə bilərsiniz."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefonunuzun tətbiqlərini yayımlayın"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefondan tətbiq və digər sistem funksiyalarını yayımlayın"</string>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index 071d93e..314d9ff 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Promena medijskog izlaza"</string>
<string name="permission_storage" msgid="6831099350839392343">"Slike i mediji"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Obaveštenja"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Upućivanje telefonskih poziva i upravljanje njima"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje evidencije poziva na telefonu"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregled SMS poruka"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Pristup kontaktima"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Pristup kalendaru"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Snimanje zvuka"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Pronalaženje uređaja u blizini, utvrđivanje njihove relativne pozicije i povezivanje sa njima"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obaveštenja, uključujući informacija poput kontakata, poruka i slika"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čitanje svih obaveštenja, uključujući informacija poput kontakata, poruka i slika<br/>• Slanje obaveštenja<br/><br/>Da biste upravljali dozvolama ove aplikacije za čitanje i slanje obaveštenja, idite u Podešavanja > Obaveštenja."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Strimujte aplikacije na telefonu"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strimujte aplikacije i druge sistemske funkcije sa telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index d99b815..fc17899 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Прылады паблізу"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Змяніць прыладу вываду медыя"</string>
<string name="permission_storage" msgid="6831099350839392343">"Фота і медыяфайлы"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Апавяшчэнні"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Праграмы"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Перадача плынню"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Ажыццяўленне тэлефонных выклікаў і кіраванне імі"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Чытанне і запіс журнала тэлефонных выклікаў"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Адпраўка SMS-паведамленняў і іх прагляд"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Доступ да вашых кантактаў"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Доступ да вашага календара"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Запіс гуку"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Пошук прылад паблізу, падключэнне да іх і вызначэнне іх адноснага месцазнаходжання"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Чытанне ўсіх апавяшчэнняў, у тым ліку такой інфармацыі, як кантакты, паведамленні і фота"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Чытанне ўсіх апавяшчэнняў, у тым ліку такой інфармацыі, як кантакты, паведамленні і фота.<br/>• Адпраўка апавяшчэнняў.<br/><br/>Дазволы гэтай праграмы на чытанне і адпраўку апавяшчэнняў можна змяніць у любы час, выбраўшы пункт меню \"Налады > Апавяшчэнні\"."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Трансляцыя змесціва праграм з вашага тэлефона"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Перадача плынню змесціва праграм і іншых функцый сістэмы з вашага тэлефона"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 3840698..c9f85dd 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Устройства в близост"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Промяна на мултимедийния изход"</string>
<string name="permission_storage" msgid="6831099350839392343">"Снимки и мултимедия"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Известия"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Приложения"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Поточно предаване"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Извършване и управление на телефонни обаждания"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Четене и запис на списъка с обажданията"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Изпращане и преглед на SMS съобщения"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Достъп до контактите ви"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Достъп до календара ви"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Записване на звук"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Намиране и свързване с устройства в близост, както и определяне на относителната им позиция"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Четене на всички известия, включително различна информация, като например контакти, съобщения и снимки"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Четене на всички известия, включително различна информация, като например контакти, съобщения и снимки.<br/>• Изпращане на известия.<br/><br/>Можете да управлявате способността на това приложение да чете и изпраща известия по всяко време в „Настройки“ > „Известия“."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Поточно предаване на приложенията на телефона ви"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Поточно предаване на приложения и други системни функции от телефона ви"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index 38521d6..c3ceda0 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"মাইক্রোফোন"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"কল লগ"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"আশেপাশের ডিভাইস"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"মিডিয়া আউটপুট পরিবর্তন করুন"</string>
<string name="permission_storage" msgid="6831099350839392343">"ফটো ও মিডিয়া"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"বিজ্ঞপ্তি"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"অ্যাপ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"স্ট্রিমিং"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ফোন কল করুন এবং ম্যানেজ করুন"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ফোন কল লগ দেখে তাতে পরিবর্তন করার অনুমতি দিন"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"এসএমএস মেসেজ দেখুন ও পাঠান"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"আপনার পরিচিতি অ্যাক্সেস করুন"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"আপনার ক্যালেন্ডার অ্যাক্সেস করার অনুমতি দিন"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"অডিও রেকর্ড করুন"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"আশেপাশের ডিভাইস খুঁজুন, তার সাথে কানেক্ট করুন এবং তার আপেক্ষিক অবস্থান নির্ধারণ করুন"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"পরিচিতি, মেসেজ এবং ফটোর মতো তথ্য সহ সমস্ত বিজ্ঞপ্তি পড়ুন"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• পরিচিতি, মেসেজ এবং ফটোর মতো তথ্য সহ সমস্ত বিজ্ঞপ্তি পড়ুন<br/>• বিজ্ঞপ্তি পাঠান<br/><br/>আপনি সেটিংস > বিজ্ঞপ্তি থেকে এই অ্যাপটির বিজ্ঞপ্তি পড়ার এবং পাঠানোর ক্ষমতা ম্যানেজ করতে পারবেন।"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"আপনার ফোনের অ্যাপ স্ট্রিম করুন"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"আপনার ফোন থেকে অ্যাপ ও অন্যান্য সিস্টেম ফিচার স্ট্রিম করে"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"উপলভ্য থাকা ডিভাইসের তালিকা অ্যাক্সেস করুন এবং কোন ডিভাইস অন্যান্য অ্যাপ থেকে অডিও বা ভিডিও স্ট্রিম অথবা কাস্ট করতে পারে তা কন্ট্রোল করুন"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"ফোন"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"ট্যাবলেট"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 6b03d24..259a06a 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Promijeni izlaz med. sadržaja"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Obavještenja"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Prijenos"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Pozivanje i upravljanje telefonskim pozivima"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje zapisnika telefonskih poziva"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregledanje SMS poruka"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Pristup vašim kontaktima"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Pristup vašem kalendaru"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Snimanje zvuka"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Pronalaženje uređaja u blizini, povezivanje s njima i određivanje njihovog relativnog položaja"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obavještenja, uključujući informacije poput kontakata, poruka i fotografija"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• čitanje svih obavještenja, uključujući informacije poput kontakata, poruka i fotografija<br/>• slanje obavještenja<br/><br/>Da upravljate mogućnošću ove aplikacije da čita i šalje obavještenja, idite u Postavke > Obavještenja."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Prenosite aplikacije s telefona"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Prijenos aplikacija i drugih funkcija sistema s vašeg telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index dcafb07c..62fe858 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositius propers"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Canvia la sortida multimèdia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos i contingut multimèdia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notificacions"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacions"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reproducció en línia"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Fer i gestionar trucades telefòniques"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Llegir i escriure el registre de trucades del telèfon"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar i llegir missatges SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Accedir als contactes"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Accedir al calendari"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Gravar àudio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Determinar la posició relativa dels dispositius propers, trobar-los i connectar-s\'hi"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Llegir totes les notificacions, inclosa informació com ara els contactes, els missatges i les fotos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Llegir totes les notificacions, inclosa informació com ara els contactes, els missatges i les fotos.<br/>• Enviar notificacions.<br/><br/>A Configuració > Notificacions, pots gestionar en qualsevol moment els permisos de l\'aplicació per llegir i enviar notificacions."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Reprodueix en continu aplicacions del telèfon"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Reprodueix en continu aplicacions i altres funcions del sistema des del telèfon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 98412c81..76c0a4a 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Zařízení v okolí"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Změna mediálního výstupu"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotky a média"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Oznámení"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikace"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streamování"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Uskutečňování a správa telefonních hovorů"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čtení seznamu hovorů a zapisování do něj"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Odesílání a zobrazování zpráv SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Přístup ke kontaktům"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Přístup ke kalendáři"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Nahrávání zvuku"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vyhledávání zařízení v okolí, připojování se k nim a zjišťování jejich relativní polohy"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čtení veškerých oznámení včetně informací, jako jsou kontakty, zprávy a fotky"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čtení veškerých oznámení, včetně informací, jako jsou kontakty, zprávy a fotky<br/>• Odesílání oznámení<br/><br/>Oprávnění této aplikace číst a odesílat oznámení můžete kdykoli změnit v Nastavení > Oznámení."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Streamujte aplikace v telefonu"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streamování aplikací a dalších systémových funkcí z telefonu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 86be312..f765c83 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Enheder i nærheden"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Skift medieoutput"</string>
<string name="permission_storage" msgid="6831099350839392343">"Billeder og medier"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notifikationer"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Foretage og administrere telefonopkald"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Læse og redigere opkaldshistorik"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Sende og se sms-beskeder"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Få adgang til dine kontakter"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Få adgang til din kalender"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Optage lyd"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finde, oprette forbindelse til og fastslå den omtrentlige lokation af enheder i nærheden"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Læse alle notifikationer, herunder oplysninger som f.eks. kontakter, beskeder og billeder"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Læse alle notifikationer, herunder oplysninger som f.eks. kontakter, beskeder og billeder<br/>• Sende notifikationer<br/><br/>Du kan til enhver tid administrere appens mulighed for at læse og sende notifikationer under Indstillinger > Notifikationer."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream din telefons apps"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stream apps og andre systemfunktioner fra din telefon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 1bc2fed..e21e8cd 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Geräte in der Nähe"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Medienausgabe ändern"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos und Medien"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Benachrichtigungen"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Anrufe starten und verwalten"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Auf die Anrufliste zugreifen und sie bearbeiten"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS senden und ansehen"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Auf deine Kontakte zugreifen"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Auf deinen Kalender zugreifen"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Audio aufnehmen"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Geräte in der Nähe finden, eine Verbindung zu ihnen herstellen und ihren relativen Standort ermitteln"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Alle Benachrichtigungen sehen, einschließlich Informationen wie Kontakten, Nachrichten und Fotos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Alle Benachrichtigungen sehen, einschließlich Informationen wie Kontakte, Nachrichten und Fotos<br/>• Benachrichtigungen senden<br/><br/>Du kannst die Berechtigungen dieser App zum Sehen und Senden von Benachrichtigungen jederzeit unter „Einstellungen > Benachrichtigungen“ ändern."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Smartphone-Apps streamen"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Apps und andere Systemfunktionen von deinem Smartphone streamen"</string>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index c8d6904..a95d063 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Συσκευές σε κοντινή απόσταση"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Αλλαγή εξόδου μέσων"</string>
<string name="permission_storage" msgid="6831099350839392343">"Φωτογραφίες και μέσα"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Ειδοποιήσεις"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Εφαρμογές"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Ροή"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Πραγματοποίηση και διαχείριση τηλεφωνικών κλήσεων"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ανάγνωση και εγγραφή αρχείου καταγραφής τηλεφωνικών κλήσεων"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Αποστολή και προβολή μηνυμάτων SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Πρόσβαση στις επαφές σας"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Πρόσβαση στο ημερολόγιό σας"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Ηχογράφηση"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Εύρεση, σύνδεση και προσδιορισμός της σχετικής τοποθεσίας συσκευών σε κοντινή απόσταση"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ανάγνωση όλων των ειδοποιήσεων, συμπεριλαμβανομένων πληροφοριών όπως επαφές, μηνύματα και φωτογραφίες"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ανάγνωση όλων των ειδοποιήσεων, συμπεριλαμβανομένων πληροφοριών όπως επαφές, μηνύματα και φωτογραφίες<br/>• Αποστολή ειδοποιήσεων<br/><br/>Μπορείτε να διαχειριστείτε τη δυνατότητα της εφαρμογής να διαβάζει και να στέλνει ειδοποιήσεις οποιαδήποτε στιγμή στις Ρυθμίσεις > Ειδοποιήσεις."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Μεταδώστε σε ροή τις εφαρμογές του τηλεφώνου σας"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Ροή εφαρμογών και άλλων λειτουργιών του συστήματος από το τηλέφωνό σας"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index e42ee5e..75cd49c 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos cercanos"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia la salida multimedia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos y contenido multimedia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notificaciones"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Transmisión"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Hacer y administrar llamadas telefónicas"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Leer y escribir el registro de llamadas telefónicas"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar y ver mensajes SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Acceder a tus contactos"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Acceder al calendario"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Grabar audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar dispositivos cercanos, conectarse a ellos y determinar su posición relativa"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Leer todas las notificaciones, incluso con información como contactos, mensajes y fotos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Leer todas las notificaciones, incluso con información como contactos, mensajes y fotos<br/>• Enviar notificaciones<br/><br/>Puedes administrar la capacidad de esta app para leer y enviar notificaciones en cualquier momento en Configuración > Notificaciones."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Transmitir las apps de tu teléfono"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Transmite apps y otras funciones del sistema desde tu teléfono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 40b0a64..ba6045b 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos cercanos"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar salida multimedia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos y elementos multimedia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notificaciones"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplicaciones"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Emitir"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Hacer y gestionar llamadas telefónicas"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Leer y escribir en el registro de llamadas del teléfono"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar y ver mensajes SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Acceder a tus contactos"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Acceder a tu calendario"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Grabar audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Buscar, conectarse y determinar la posición relativa de dispositivos cercanos"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Leer todas las notificaciones, incluida información como contactos, mensajes y fotos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Leer todas las notificaciones, incluida información como contactos, mensajes y fotos<br/>• Enviar notificaciones<br/><br/>Puedes gestionar los permisos de esta aplicación para leer y enviar notificaciones cuando quieras en Ajustes > Notificaciones."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Muestra en streaming las aplicaciones de tu teléfono"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emite aplicaciones y otras funciones del sistema desde tu teléfono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 8a436b3..dc8d992 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Läheduses olevad seadmed"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Muutke meediaväljundit"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotod ja meedia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Märguanded"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Rakendused"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Voogesitus"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Helistamine ja telefonikõnede haldamine"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefoni kõnelogi lugemine ja kirjutamine"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS-sõnumite saatmine ja vaatamine"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Juurdepääs kontaktidele"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Juurdepääs kalendrile"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Heli salvestamine"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Läheduses olevate seadmete leidmine, nendega ühenduse loomine ja nende suhtelise asendi määramine"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kõikide märguannete, sh sellise teabe nagu kontaktid, sõnumid ja fotod lugemine"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kõikide märguannete, sh sellise teabe nagu kontaktid, sõnumid ja fotod lugemine<br/>• Märguannete saatmine<br/><br/>Saate selle rakenduse võimalusi märguannete lugemiseks ja saatmiseks igal ajal hallata jaotises Seaded > Märguanded."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefoni rakenduste voogesitamine"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Rakenduste ja muude süsteemi funktsioonide voogesitamine teie telefonist"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 38f0512..8cd4fde 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Inguruko gailuak"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Aldatu multimedia-irteera"</string>
<string name="permission_storage" msgid="6831099350839392343">"Argazkiak eta multimedia-edukia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Jakinarazpenak"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikazioak"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Igortzea"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Telefono-deiak egin eta kudeatu"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefonoko deien erregistroa irakurri eta bertan idatzi"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mezuak bidali eta ikusi"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Kontaktuak atzitu"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Egutegia atzitu"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Audioa grabatu"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Inguruko gailuak aurkitu, haietara konektatu eta haien posizio erlatiboa zehaztu"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Jakinarazpen guztiak irakurri; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Jakinarazpen guztiak irakurri; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa.<br/>• Jakinarazpenak bidali.<br/><br/>Aplikazioak jakinarazpenak irakurri eta bidaltzeko dituen baimenak kudeatzeko, joan Ezarpenak > Jakinarazpenak atalera."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Igorri zuzenean telefonoko aplikazioak"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Igorri aplikazioak eta sistemaren beste eginbide batzuk telefonotik"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 5d0ad19..83628cb 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"دستگاههای اطراف"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"تغییر دادن خروجی رسانه"</string>
<string name="permission_storage" msgid="6831099350839392343">"عکسها و رسانهها"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"اعلانها"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"برنامهها"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"جاریسازی"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"برقراری و مدیریت تماسهای تلفنی"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"خواندن و نوشتن گزارش تماس تلفنی"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"ارسال و مشاهده پیامکها"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"دسترسی به مخاطبین"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"دسترسی به تقویم"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ضبط کردن صدا"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"پیدا کردن، تعیین موقعیت نسبی، و متصل شدن به دستگاههای اطراف"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"خواندن همه اعلانها، شامل اطلاعاتی مثل مخاطبین، پیامها، و عکسها"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• خواندن همه اعلانها، شامل اطلاعاتی مثل مخاطبین، پیامها، و عکسها<br/>• ارسال اعلان<br/><br/>هرزمان بخواهید میتوانید این توانایی برنامه برای خواندن و ارسال اعلان را در «تنظیمات > اعلانها» مدیریت کنید."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"جاریسازی برنامههای تلفن"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"برنامهها و دیگر ویژگیهای سیستم را از تلفن شما جاریسازی میکند"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index b40de5f..86e7904 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Lähellä olevat laitteet"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Median ulostulon muuttaminen"</string>
<string name="permission_storage" msgid="6831099350839392343">"Kuvat ja media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Ilmoitukset"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Sovellukset"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striimaus"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"soittaa ja hallinnoida puheluita"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"lukea puhelulokia ja kirjoittaa siihen"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"lähettää ja lukea tekstiviestejä"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"käyttää yhteystietoja"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"käyttää kalenteria"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"tallentaa audiota"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"löytää lähellä olevia laitteita, muodostaa niihin yhteyden ja määrittää niiden suhteellisen sijainnin"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"lukea kaikkia ilmoituksia, esim. yhteystietoihin, viesteihin ja kuviin liittyviä tietoja"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• lukea kaikkia ilmoituksia, esim. yhteystietoihin, viesteihin ja kuviin liittyviä tietoja<br/>• lähettää ilmoituksia.<br/><br/>Voit milloin tahansa valita, saako sovellus lukea ja lähettää ilmoituksia, avaamalla Asetukset > Ilmoitukset."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Striimaa puhelimen sovelluksia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Striimaa sovelluksia ja muita järjestelmän ominaisuuksia puhelimesta"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 9b5707c..b74c8ee 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"Microphone"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Journaux d\'appels"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Appareils à proximité"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Modifier la sortie multimédia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Photos et fichiers multimédias"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Applications"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Diffusion en continu"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Passez et gérez des appels téléphoniques"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lisez et écrivez le journal d\'appels téléphoniques"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Envoyez et affichez des messages texte"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Accédez à vos contacts"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Accédez à votre agenda"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Enregistrez des fichiers audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trouvez et déterminez la position relative des appareils à proximité, et connectez-vous à ceux-ci"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lisez toutes les notifications, y compris les renseignements tels que les contacts, les messages et les photos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lisez toutes les notifications, y compris les renseignements tels que les contacts, les messages et les photos<br/>• Envoyez des notifications<br/><br/>Vous pouvez gérer la capacité de cette application à lire et à envoyer des notifications à tout moment dans Paramètres > Notifications."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Diffusez les applications de votre téléphone"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Diffusez des applications et d\'autres fonctionnalités du système à partir de votre téléphone"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accédez à une liste d\'appareils accessibles et contrôlez ceux qui diffusent du contenu audio ou vidéo à partir d\'autres applications"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"téléphone"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"tablette"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 3437988..30b0c9a 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Appareils à proximité"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Modifier la sortie multimédia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Photos et contenus multimédias"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Applis"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Effectuer et gérer des appels téléphoniques"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Consulter et créer les journaux d\'appels du téléphone"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Envoyer et consulter des SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Accéder à vos contacts"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Accéder à votre agenda"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Enregistrer un fichier audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trouver les appareils à proximité, s\'y connecter et déterminer leur position relative"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lire toutes les notifications, y compris des informations comme les contacts, messages et photos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lire toutes les notifications, y compris les informations comme les contacts, les messages et les photos<br/>• Envoyer des notifications<br/><br/>Vous pouvez gérer les autorisations de lecture et d\'envoi des notifications à tout moment dans Paramètres > Notifications."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Diffuser en streaming les applis de votre téléphone"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Diffusez des applis et d\'autres fonctionnalités système en streaming depuis votre téléphone"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index e84f2d9..1b8b8d4 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos próximos"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar a saída multimedia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos e contido multimedia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notificacións"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacións"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reprodución en tempo real"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Fai e xestiona chamadas telefónicas"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Le e edita o rexistro de chamadas do teléfono"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Envía e consulta mensaxes SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Accede aos contactos"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Accede ao calendario"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Grava audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Busca dispositivos próximos, establece conexión con eles e determina a súa posición relativa"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Le todas as notificacións (que poden incluír información como contactos, mensaxes e fotos)"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Le todas as notificacións (que poden incluír información como contactos, mensaxes e fotos)<br/>• Envía notificacións<br/><br/>En Configuración > Notificacións podes xestionar en calquera momento a capacidade desta aplicación para ler e enviar notificacións."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Emite as aplicacións do teu teléfono"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emite o contido das aplicacións e doutras funcións do sistema desde o teléfono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 62e518a..474960c 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"નજીકના ડિવાઇસ"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"મીડિયા આઉટપુટ બદલો"</string>
<string name="permission_storage" msgid="6831099350839392343">"ફોટા અને મીડિયા"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"નોટિફિકેશન"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ઍપ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"સ્ટ્રીમિંગ"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ફોન કૉલ કરવાની અને મેનેજ કરવાની"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ફોન કૉલ લૉગ વાંચવાની અને લખવાની"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS મેસેજ મોકલવાની અને જોવાની"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"તમારા સંપર્કોને ઍક્સેસ કરવાની"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ઑડિયો રેકોર્ડ કરવાની"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"નજીકના ડિવાઇસ શોધવાની, તેમની સાથે કનેક્ટ કરવાની અને તેમની સંબંધિત સ્થિતિ નિર્ધારિત કરવાની"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"સંપર્કો, મેસેજ અને ફોટા જેવી માહિતી સહિતના બધા નોટિફિકેશન વાંચવાની"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• સંપર્કો, મેસેજ અને ફોટા જેવી માહિતી સહિતના બધા નોટિફિકેશન વાંચવાની<br/>• નોટિફિકેશન મોકલવાની<br/><br/>તમે કોઈપણ સમયે સેટિંગમાં > નોટિફિકેશનમાં જઈને આ ઍપની નોટિફિકેશન વાંચવાની અને મોકલવાની ક્ષમતાને મેનેજ કરી શકો છો."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"તમારા ફોનની ઍપ સ્ટ્રીમ કરો"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"તમારા ફોન પરથી ઍપ અને સિસ્ટમની અન્ય સુવિધાઓ સ્ટ્રીમ કરો"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 3d3a2c1..931eab6 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"आस-पास मौजूद डिवाइस"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट बदलें"</string>
<string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"सूचनाएं"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ऐप्लिकेशन"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"स्ट्रीमिंग"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"फ़ोन कॉल करने और उन्हें मैनेज करने की अनुमति दें"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"कॉल लॉग की जानकारी देखने और उसमें बदलाव करने की अनुमति दें"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"एसएमएस (मैसेज) भेजने और देखने की अनुमति दें"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"अपने संपर्कों को ऐक्सेस करने की अनुमति दें"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"अपने कैलेंडर को ऐक्सेस करने की अनुमति दें"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ऑडियो रिकॉर्ड करने की अनुमति दें"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"आस-पास मौजूद डिवाइसों को खोजने, उनसे कनेक्ट करने, और उनकी जगह की जानकारी का पता लगाने की अनुमति दें"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"सभी सूचनाएं देखने की अनुमति दें. जैसे, संपर्क, मैसेज, और फ़ोटो की जानकारी"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• सभी सूचनाएं देखने की अनुमति दें. जैसे, संपर्क, मैसेज, और फ़ोटो की जानकारी<br/>• सूचनाएं भेजने की अनुमति दें<br/><br/>सेटिंग > सूचनाएं में जाकर इस ऐप्लिकेशन के लिए, सूचनाओं को देखने और उनमें बदलाव करने की अनुमति में कभी भी बदलाव किया जा सकता है."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"अपने फ़ोन पर मौजूद ऐप्लिकेशन स्ट्रीम करें"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"अपने फ़ोन से ऐप्लिकेशन और सिस्टम की दूसरी सुविधाओं को स्ट्रीम करें"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index d6fbf2f..6dc59ea 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Uređaji u blizini"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Promjena medijskog izlaza"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Obavijesti"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Uspostavljanje telefonskih poziva i upravljanje njima"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje zapisnika poziva telefona"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregledavanje SMS poruka"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Pristup kontaktima"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Pristup kalendaru"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Snimanje zvuka"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Pronalaženje i određivanje relativnog položaja uređaja u blizini i povezivanje s njima"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obavijesti, uključujući podatke kao što su kontakti, poruke i fotografije"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čitanje svih obavijesti, uključujući podatke kao što su kontakti, poruke i fotografije<br/>• Slanje obavijesti<br/><br/>Uvijek možete upravljati mogućnostima ove aplikacije da čita i šalje obavijesti u postavkama i obavijestima"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Streaming aplikacija vašeg telefona"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emitirajte stream aplikacija i drugih značajki sustava s vašeg telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index 0db1f70..4b1a6f7 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Közeli eszközök"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Médiakiment módosítása"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotók és médiatartalmak"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Értesítések"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Alkalmazások"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streamelés"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Telefonhívások kezdeményezése és kezelése"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Hívásnapló olvasása és írása"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS-ek küldése és megtekintése"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Hozzáférés a névjegyekhez"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Hozzáférés a naptárhoz"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Hang rögzítése"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Megkeresheti a közeli eszközöket, meghatározhatja viszonylagos helyzetüket és csatlakozhat hozzájuk."</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Elolvashat minden értesítést, ideértve az olyan információkat, mint a névjegyek, az üzenetek és a fotók."</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Elolvashat minden értesítést, ideértve az olyan információkat, mint a névjegyek, az üzenetek és a fotók.<br/>• Értesítéseket küldhet.<br/><br/>A Beállítások > Értesítések menüpontban bármikor kezelheti az alkalmazás értesítések olvasására és küldésére való képességét."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"A telefon alkalmazásainak streamelése"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Alkalmazások és más rendszerfunkciók streamelése a telefonról"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 6e08a97..405a983 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"Խոսափող"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Կանչերի ցուցակ"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Մոտակա սարքեր"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Փոխել մեդիա արտածումը"</string>
<string name="permission_storage" msgid="6831099350839392343">"Լուսանկարներ և մուլտիմեդիա"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Ծանուցումներ"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Հավելվածներ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Հեռարձակում"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Կատարել զանգեր և կառավարել զանգերը"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Դիտել հեռախոսի զանգերի մատյանը և կատարել գրանցում"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Ուղարկել և կարդալ SMS հաղորդագրություններ"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Օգտագործել ձեր կոնտակտները"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Օգտագործել ձեր օրացույցը"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Ձայնագրել"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Գտնել և որոշել մոտակա սարքերի մոտավոր դիրքը և միանալ դրանց"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Կարդալ բոլոր ծանուցումները, ներառյալ տեղեկությունները, օրինակ՝ կոնտակտները, հաղորդագրությունները և լուսանկարները"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Կարդալ բոլոր ծանուցումները, այդ թվում՝ կոնտակտները, հաղորդագրությունները և լուսանկարները<br/>• Ուղարկել ծանուցումներ<br/><br/>Դուք ցանկացած պահի կարող եք կառավարել այս հավելվածի՝ ծանուցումներ կարդալու հնարավորությունը՝ անցնելով Կարգավորումներ > Ծանուցումներ։"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Հեռարձակել հեռախոսի հավելվածները"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Հեռարձակել հավելվածներ և համակարգի այլ գործառույթներ հեռախոսում"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Բացել հասանելի սարքերի ցանկը և կառավարել, թե որ սարքը աուդիո կամ վիդեո հեռարձակի այլ հավելվածներից"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"հեռախոս"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"պլանշետ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index c0b8245..8f3909c 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Perangkat di sekitar"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Mengubah output media"</string>
<string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notifikasi"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikasi"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Melakukan dan mengelola panggilan telepon"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Membaca dan menulis log panggilan telepon"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Mengirim dan melihat pesan SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Mengakses kontak"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Mengakses kalender"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Merekam audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Menemukan, menghubungkan, dan menentukan posisi relatif dari perangkat di sekitar"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Membaca semua notifikasi, termasuk informasi seperti kontak, pesan, dan foto"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Membaca semua notifikasi, termasuk informasi seperti kontak, pesan, dan foto<br/>• Mengirim notifikasi<br/><br/>Anda dapat mengelola kemampuan aplikasi untuk membaca dan mengirim notifikasi kapan saja di Setelan > Notifikasi."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Streaming aplikasi ponsel"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Menstreaming aplikasi dan fitur sistem lainnya dari ponsel Anda"</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index 5951a1d..baad2cf 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Nálæg tæki"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Breyta margmiðlunarúttaki"</string>
<string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Tilkynningar"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Forrit"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streymi"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Hringja og stjórna símtölum"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lesa og skrifa símtalaskrá símans"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Senda og skoða SMS-skilaboð"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Fá aðgang að tengiliðunum þínum"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Fá aðgang að dagatalinu þínu"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Taka upp hljóð"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finna, tengjast og áætla staðsetningu nálægra tækja"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lesa allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði, skilaboð og myndir"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lesa allar tilkynningar, þ.m.t. upplýsingar á borð við tengiliði, skilaboð og myndir<br/>• Senda tilkynningar<br/><br/>Þú getur stjórnað getu forritsins til að lesa og senda tilkynningar hvenær sem er í „Stillingar > Tilkynningar“."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Streymdu forritum símans"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streymdu forritum og öðrum kerfiseiginleikum úr símanum"</string>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 9ce1241..81f5d62 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivi nelle vicinanze"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia uscita conten. multim."</string>
<string name="permission_storage" msgid="6831099350839392343">"Foto e contenuti multimediali"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notifiche"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"App"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Esegue e gestisce le telefonate"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Legge e modifica il registro chiamate dello smartphone"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Invia e visualizza SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Accede ai tuoi contatti"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Accede al tuo calendario"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Registra l\'audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trova e si connette ai dispositivi nelle vicinanze, oltre a stabilire la loro posizione relativa"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Legge tutte le notifiche, incluse informazioni come contatti, messaggi e foto"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Legge tutte le notifiche, incluse informazioni come contatti, messaggi e foto<br/>• Invia notifiche<br/><br/>Puoi gestire la capacità dell\'app di leggere e inviare notifiche in qualsiasi momento in Impostazioni > Notifiche."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Trasmetti in streaming le app del tuo telefono"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Trasmettere in streaming app e altre funzionalità di sistema dal telefono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 8e2b715..f4367c0 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"מכשירים בקרבת מקום"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"שינוי פלט המדיה"</string>
<string name="permission_storage" msgid="6831099350839392343">"תמונות ומדיה"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"התראות"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"אפליקציות"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"סטרימינג"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ביצוע וניהול של שיחות טלפון"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"קריאה וכתיבה של נתוני יומן השיחות בטלפון"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"שליחה של הודעות SMS וצפייה בהן"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"גישה אל אנשי הקשר"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"גישה אל היומן"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"הקלטת אודיו"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"אפשרות למצוא מכשירים בקרבת מקום, להתחבר אליהם ולאתר את המיקום היחסי שלהם"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"אפשרות לקרוא את כל ההתראות, כולל מידע כמו אנשי קשר, הודעות ותמונות"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• קריאה של כל ההתראות, כולל מידע כמו אנשי קשר, הודעות ותמונות<br/>• שליחת התראות<br/><br/>אפשר לשנות את היכולת של האפליקציה לקרוא ולשלוח התראות בכל שלב ב\'הגדרות\' > \'התראות\'."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"שידור אפליקציות מהטלפון"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"העברה של אפליקציות ותכונות מערכת אחרות בסטרימינג מהטלפון"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index 175ea0f..2dfb4d1 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"付近のデバイス"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"メディア出力の変更"</string>
<string name="permission_storage" msgid="6831099350839392343">"写真とメディア"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"アプリ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ストリーミング"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"電話の発信と管理"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"通話履歴の読み取りと書き込み"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS メッセージの送信と表示"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"連絡先へのアクセス"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"カレンダーへのアクセス"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"録音"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"付近のデバイスの検出、接続、相対位置の特定"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"すべての通知の読み取り(連絡先、メッセージ、写真に関する情報を含む)"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• すべての通知の読み取り(連絡先、メッセージ、写真に関する情報を含む)<br/>• 通知の送信<br/><br/>このアプリの通知読み取り機能と通知送信機能はいつでも [設定] > [通知] で管理できます。"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"スマートフォンのアプリをストリーミングします"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"アプリやその他のシステム機能をスマートフォンからストリーミングする"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index c655344..f4dcaea 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"ახლომახლო მოწყობილობები"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"გამომავალი მედიის გამოტანის"</string>
<string name="permission_storage" msgid="6831099350839392343">"ფოტოები და მედია"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"შეტყობინებები"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"აპები"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"სტრიმინგი"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"სატელეფონო ზარების განხორციელება და მართვა"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"სატელეფონო ზარების ჟურნალის წაკითხვა და მასში ჩაწერა"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS შეტყობინებების გაგზავნა და ნახვა"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"თქვენს კონტაქტებზე წვდომა"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"თქვენს კალენდარზე წვდომა"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"აუდიოს ჩაწერა"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ახლომახლო მოწყობილობების პოვნა, მათთან დაკავშირება და მათი შედარებითი პოზიციის დადგენა"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ყველა შეტყობინების, მათ შორის ისეთი ინფორმაციის წაკითხვა, როგორიცაა კონტაქტები, შეტყობინებები და ფოტოები"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• ყველა შეტყობინების, მათ შორის კონტაქტების, შეტყობინებებისა და ფოტოების წაკითხვა<br/>• შეტყობინებების გაგზავნა<br/><br/>შეგიძლიათ, მართოთ ამ აპის შესაძლებლობა, წაიკითხოს და გაგზავნოს შეტყობინებები ნებისმიერ დროს პარამეტრებში > შეტყობინებები."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"თქვენი ტელეფონის აპების სტრიმინგი"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"აწარმოეთ აპების და სისტემის სხვა ფუნქციების სტრიმინგი თქვენი ტელეფონიდან"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index c4928da..122e2e6 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Маңайдағы құрылғылар"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Мультимедиа шығысын өзгерту"</string>
<string name="permission_storage" msgid="6831099350839392343">"Фотосуреттер мен медиафайлдар"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Хабарландырулар"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Қолданбалар"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Трансляция"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Қоңырау шалу және телефон қоңырауларын басқару"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Телефонның қоңыраулар журналын оқу және жазу"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS хабарларын жіберу және көру"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Контактілерге кіру"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Күнтізбеге кіру"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Аудио жазу"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Маңайдағы құрылғыларды тауып, олармен байланысып, бір-біріне қатысты локациясын анықтайды."</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Барлық хабарландыруды, соның ішінде контактілер, хабарлар және фотосуреттер сияқты ақпаратты оқиды."</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Барлық хабарландыруды, соның ішінде контактілер, хабарлар және фотосуреттер сияқты ақпаратты оқиды<br/>• Хабарландырулар жібереді<br/><br/>Бұл қолданбаның хабарландыруларды оқу және жіберу мүмкіндігін \"Параметрлер > Хабарландырулар\" тармағында кез келген уақытта басқара аласыз."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Телефон қолданбаларын трансляциялайды."</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Қолданбалар мен басқа да жүйе функцияларын телефоннан трансляциялау"</string>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 28b6340..d6f3037 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"ឧបករណ៍នៅជិត"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"ប្ដូរឧបករណ៍មេឌៀ"</string>
<string name="permission_storage" msgid="6831099350839392343">"រូបថត និងមេឌៀ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"ការជូនដំណឹង"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"កម្មវិធី"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ការផ្សាយ"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ហៅទូរសព្ទ និងគ្រប់គ្រងការហៅទូរសព្ទ"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"អាន និងសរសេរកំណត់ហេតុហៅទូរសព្ទ"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"ផ្ញើ និងមើលសារ SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"ចូលប្រើទំនាក់ទំនងរបស់អ្នក"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"ចូលប្រើប្រតិទិនរបស់អ្នក"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ថតសំឡេង"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ស្វែងរក ភ្ជាប់ និងកំណត់ទីតាំងដែលពាក់ព័ន្ធរបស់ឧបករណ៍នៅជិត"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"អានការជូនដំណឹងទាំងអស់ រួមទាំងព័ត៌មានដូចជា ទំនាក់ទំនង សារ និងរូបថតជាដើម"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• អានការជូនដំណឹងទាំងអស់ រួមទាំងព័ត៌មានដូចជា ទំនាក់ទំនង សារ និងរូបថតជាដើម<br/>• ផ្ញើការជូនដំណឹង<br/><br/>អ្នកអាចគ្រប់គ្រងលទ្ធភាពរបស់កម្មវិធីនេះក្នុងការអាន និងផ្ញើការជូនដំណឹងបានគ្រប់ពេលនៅក្នុងការកំណត់ > ការជូនដំណឹង។"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"ផ្សាយកម្មវិធីរបស់ទូរសព្ទអ្នក"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ចាក់ផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធផ្សេងទៀតពីទូរសព្ទរបស់អ្នក"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index eb94178..661d344 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳು"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"ಮೀಡಿಯಾ ಔಟ್ಪುಟ್ ಬದಲಾಯಿಸಿ"</string>
<string name="permission_storage" msgid="6831099350839392343">"ಫೋಟೋಗಳು ಮತ್ತು ಮಾಧ್ಯಮ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"ನೋಟಿಫಿಕೇಶನ್ಗಳು"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ಆ್ಯಪ್ಗಳು"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ಸ್ಟ್ರೀಮಿಂಗ್"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಿ ಹಾಗೂ ನಿರ್ವಹಿಸಿ"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ಪೋನ್ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಓದಿ ಮತ್ತು ಬರೆಯಿರಿ"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಿ ಮತ್ತು ವೀಕ್ಷಿಸಿ"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಅನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳನ್ನು ಹುಡುಕಬಹುದು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಆಗಬಹುದು ಮತ್ತು ಅವುಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಬಹುದು"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ಸಂಪರ್ಕಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಫೋಟೋಗಳಂತಹ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಓದಬಹುದು"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• ಸಂಪರ್ಕಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಫೋಟೋಗಳಂತಹ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಓದಿ<br/>• ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಕಳುಹಿಸಿ<br/><br/>ಈ ಆ್ಯಪ್ನ ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಓದುವ ಮತ್ತು ಕಳುಹಿಸುವ ಸಾಮರ್ಥ್ಯವನ್ನು ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್ಗಳು > ನೋಟಿಫಿಕೇಶನ್ಗಳಲ್ಲಿ ನಿರ್ವಹಿಸಬಹುದು."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"ನಿಮ್ಮ ಫೋನ್ನ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಿ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ನಿಮ್ಮ ಫೋನ್ನಿಂದ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಇತರ ಸಿಸ್ಟಂ ಫೀಚರ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಿ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 4685397..aefda7c 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"근처 기기"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"미디어 출력 변경"</string>
<string name="permission_storage" msgid="6831099350839392343">"사진 및 미디어"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"알림"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"앱"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"스트리밍"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"전화 걸기 및 관리"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"통화 기록 읽고 쓰기"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS 메시지 보내기 및 보기"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"연락처에 액세스"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"캘린더에 액세스"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"오디오 녹음"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"근처 기기를 찾아 연결하고 기기 간 상대 위치를 파악할 수 있습니다."</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"연락처, 메시지, 사진 등의 정보를 포함한 모든 알림을 읽을 수 있습니다."</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• 연락처, 메시지, 사진과 같은 정보를 포함해 모든 알림을 읽습니다.<br/>• 알림을 전송합니다.<br/><br/>언제든지 설정 > 알림에서 알림을 읽고 전송할 수 있는 이 앱의 능력을 관리할 수 있습니다."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"휴대전화의 앱을 스트리밍합니다."</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"내 휴대전화의 앱 및 기타 시스템 기능 스트리밍"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 76aefe1..099b5c5 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Жакын жердеги түзмөктөр"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Медиа чыгарылышын өзгөртүү"</string>
<string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиафайлдар"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Билдирмелер"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Колдонмолор"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Алып ойнотуу"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Телефон чалуу жана аларды башкаруу"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Телефондогу чалуулар тизмесин окуу жана жазуу"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS билдирүүлөрдү жөнөтүү жана көрсөтүү"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Байланыштарыңызды көрүү"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Жылнаамаңызды пайдалануу"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Аудио жаздыруу"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Жакын жердеги түзмөктөрдү таап, аларга туташып, абалын аныктоо"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Бардык билдирмелерди, анын ичинде байланыштар, билдирүүлөр жана сүрөттөр сыяктуу маалыматты окуу"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Бардык билдирмелерди, анын ичинде байланыштар, билдирүүлөр жана сүрөттөр сыяктуу маалыматты окуу<br/>• Билдирмелерди жөнөтүү<br/><br/>Бул колдонмонун билдирмелерди окуу жана жөнөтүү мүмкүнчүлүгүн каалаган убакта Параметрлер > Билдирмелер бөлүмүнөн тескей аласыз."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Телефондогу колдонмолорду алып ойнотуу"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Телефонуңуздагы колдонмолорду жана системанын башка функцияларын алып ойнотуу"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index bbf0be3..fa378ca 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"ອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງ"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"ປ່ຽນເອົ້າພຸດສື່"</string>
<string name="permission_storage" msgid="6831099350839392343">"ຮູບພາບ ແລະ ມີເດຍ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"ການແຈ້ງເຕືອນ"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ແອັບ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ກຳລັງສະຕຣີມ"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ໂທອອກ ແລະ ຈັດການການໂທ"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ອ່ານ ແລະ ຂຽນບັນທຶກການໂທຂອງໂທລະສັບ"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"ສົ່ງ ແລະ ເບິ່ງຂໍ້ຄວາມ SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"ເຂົ້າເຖິງລາຍຊື່ຜູ້ຕິດຕໍ່ຂອງທ່ານ"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"ເຂົ້າເຖິງປະຕິທິນຂອງທ່ານ"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ບັນທຶກສຽງ"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ຊອກຫາ, ເຊື່ອມຕໍ່ ແລະ ລະບຸສະຖານທີ່ທີ່ກ່ຽວຂ້ອງກັນຂອງອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງ"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ອ່ານການແຈ້ງເຕືອນທັງໝົດ, ຮວມທັງຂໍ້ມູນ ເຊັ່ນ: ລາຍຊື່ຜູ້ຕິດຕໍ່, ຂໍ້ຄວາມ ແລະ ຮູບພາບ"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• ອ່ານການແຈ້ງເຕືອນທັງໝົດ, ຮວມທັງຂໍ້ມູນ ເຊັ່ນ: ລາຍຊື່ຜູ້ຕິດຕໍ່, ຂໍ້ຄວາມ ແລະ ຮູບພາບ<br/>• ສົ່ງການແຈ້ງເຕືອນ<br/><br/>ທ່ານສາມາດຈັດການຄວາມສາມາດຂອງແອັບນີ້ໃນການອ່ານ ແລະ ສົ່ງການແຈ້ງເຕືອນໄດ້ທຸກເວລາໃນການຕັ້ງຄ່າ> ການແຈ້ງເຕືອນ."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"ສະຕຣີມແອັບຂອງໂທລະສັບທ່ານ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ສະຕຣີມແອັບ ແລະ ຄຸນສົມບັດລະບົບອື່ນໆຈາກໂທລະສັບຂອງທ່ານ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 1c28f14..54f8408 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Įrenginiai netoliese"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Keisti medijos išvestį"</string>
<string name="permission_storage" msgid="6831099350839392343">"Nuotraukos ir medija"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Pranešimai"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Programos"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Srautinis perdavimas"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Skambinti ir tvarkyti telefonų skambučius"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Skaityti ir rašyti telefono skambučių žurnalą"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Siųsti ir peržiūrėti SMS pranešimus"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Pasiekti kontaktus"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Pasiekti kalendorių"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Įrašyti garsą"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Rasti apytikslę netoliese esančių įrenginių poziciją, aptikti juos ir prisijungti prie jų"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Skaityti visus pranešimus, įskaitant tokią informaciją kaip kontaktai, pranešimai ir nuotraukos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Skaityti visus pranešimus, įskaitant tokią informaciją kaip kontaktai, pranešimai ir nuotraukos<br/>• Siųsti pranešimus<br/><br/>Galite bet kada tvarkyti šios programos leidimą skaityti ir siųsti pranešimus skiltyje „Nustatymai“ > „Pranešimai“."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefono programų perdavimas srautu"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Srautu perduokite programas ir kitas sistemos funkcijas iš telefono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index 5126812..390d544 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Tuvumā esošas ierīces"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Mainīt multivides izvadi"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotoattēli un multivides faili"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Paziņojumi"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Lietotnes"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Straumēšana"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Veikt un pārvaldīt tālruņa zvanus"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lasīt un rakstīt tālruņa zvanu žurnālu"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Sūtīt un skatīt īsziņas"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Piekļūt jūsu kontaktpersonu datiem"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Piekļūt jūsu kalendāram"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Ierakstīt audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Atrast tuvumā esošas ierīces, izveidot ar tām savienojumu un noteikt to relatīvo atrašanās vietu"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lasīt visus paziņojumus, tostarp tādu informāciju kā kontaktpersonas, ziņojumi un fotoattēli"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lasīt visus paziņojumus, tostarp tādu informāciju kā kontaktpersonas, ziņojumi un fotoattēli<br/>• Sūtīt paziņojumus<br/><br/>Jebkurā brīdī sadaļā “Iestatījumi un paziņojumi” varat pārvaldīt šīs lietotnes atļauju lasīt un sūtīt paziņojumus."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Straumēt jūsu tālruņa lietotnes"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"No sava tālruņa straumējiet lietotnes un citas sistēmas funkcijas"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 24fbaa7..dc15899 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Уреди во близина"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Промена на излезот за аудио"</string>
<string name="permission_storage" msgid="6831099350839392343">"Аудиовизуелни содржини"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Известувања"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Апликации"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Стриминг"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Упатува и управува со телефонски повици"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Чита и пишува евиденција на повици во телефонот"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Испраќа и прикажува SMS-пораки"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Пристапува до контактите"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Пристапува до календарот"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Снима аудио"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Наоѓа и се поврзува со уреди во близина и да ја утврдува нивната релативна положба"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ги чита сите известувања, меѓу кои и податоци како контакти, пораки и фотографии"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ги чита сите известувања, меѓу кои и податоци како контакти, пораки и фотографии<br/>• Испраќа известувања<br/><br/>Може да управувате со способноста на апликацијава да чита и испраќа известувања кога било во „Поставки > Известувања“."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Стримувајте ги апликациите на телефонот"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Апликации за стриминг и други системски функции од вашиот телефон"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index a3e07ca..6c09e63 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"സമീപമുള്ള ഉപകരണങ്ങൾ"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"മീഡിയ ഔട്ട്പുട്ട് മാറ്റുക"</string>
<string name="permission_storage" msgid="6831099350839392343">"ഫോട്ടോകളും മീഡിയയും"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"അറിയിപ്പുകൾ"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ആപ്പുകൾ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"സ്ട്രീമിംഗ്"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ഫോൺ കോളുകൾ വിളിക്കുക, മാനേജ് ചെയ്യുക"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ഫോൺ കോൾ ചരിത്രം എഴുതുകയും വായിക്കുകയും ചെയ്യുക"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS സന്ദേശങ്ങൾ അയയ്ക്കുക, കാണുക"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"നിങ്ങളുടെ കോൺടാക്റ്റുകൾ ആക്സസ് ചെയ്യുക"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"നിങ്ങളുടെ കലണ്ടർ ആക്സസ് ചെയ്യുക"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"സമീപമുള്ള ഉപകരണങ്ങൾ കണ്ടെത്താനും അവയിലേക്ക് കണക്റ്റ് ചെയ്യാനും അവയുടെ ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും കഴിയും"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"കോൺടാക്റ്റുകൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഉൾപ്പെടെ എല്ലാ അറിയിപ്പുകളും വായിക്കുക"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• കോൺടാക്റ്റുകൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഉൾപ്പെടെ എല്ലാ അറിയിപ്പുകളും വായിക്കുക<br/>• അറിയിപ്പുകൾ അയയ്ക്കുക<br/><br/>അറിയിപ്പുകൾ വായിക്കാനും അയയ്ക്കാനുമുള്ള ഈ ആപ്പിന്റെ ശേഷി ക്രമീകരണവും അറിയിപ്പുകളും എന്നതിൽ ഏതുസമയത്തും മാനേജ് ചെയ്യാം."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"നിങ്ങളുടെ ഫോണിലെ ആപ്പുകൾ സ്ട്രീം ചെയ്യുക"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ആപ്പുകളും മറ്റ് സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാം"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 584185e..4cbccb4 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Ойролцоох төхөөрөмжүүд"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Медиа гаралтыг өөрчлөх"</string>
<string name="permission_storage" msgid="6831099350839392343">"Зураг болон медиа"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Мэдэгдэл"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Аппууд"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Дамжуулах"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Утасны дуудлага хийх болон удирдах"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Утасны дуудлагын жагсаалтыг унших болон бичих"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS мессеж илгээх болон харах"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Таны харилцагчдад хандах"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Таны календарьт хандах"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Аудио бичих"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Ойролцоох төхөөрөмжүүдийн харьцангуй байрлалыг олох, тодорхойлох болон тэдгээрт холбогдох"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Харилцагчид, мессеж болон зургууд зэрэг мэдээллийг оруулаад бүх мэдэгдлийг унших"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Харилцагчид, мессеж болон зургууд зэрэг мэдээллийг оруулаад бүх мэдэгдлийг унших<br/>• Мэдэгдлүүдийг илгээх<br/><br/>Та энэ аппын мэдэгдлүүдийг унших болон илгээх чадамжийг Тохиргоо > мэдэгдлүүдэд хүссэн үедээ удирдах боломжтой."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Утасныхаа аппуудыг дамжуулаарай"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Утаснаасаа аппууд болон системийн бусад онцлогийг дамжуулаарай"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index de4f7fd..a01759b 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"जवळपासची डिव्हाइस"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट बदला"</string>
<string name="permission_storage" msgid="6831099350839392343">"फोटो आणि मीडिया"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"सूचना"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ॲप्स"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"स्ट्रीमिंग"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"फोन कॉल करणे आणि ते व्यवस्थापित करणे"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"फोन कॉल लॉग रीड अँड राइट करणे"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"एसएमएस पाठवणे आणि पाहणे"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"तुमचे संपर्क ॲक्सेस करणे"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"तुमचे कॅलेंडर अॅक्सेस करणे"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ऑडिओ रेकॉर्ड करणे"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"जवळपासची डिव्हाइस शोधणे, त्यांच्याशी कनेक्ट करणे आणि त्यांचे संबंधित स्थान निर्धारित करणे"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"संपर्क, मेसेज आणि फोटो यांसारख्या माहितीसह सर्व सूचना वाचणे"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• संपर्क, मेसेज आणि फोटोसारख्या माहितीसह सर्व सूचना वाचणे<br/>• सूचना पाठवणे<br/><br/>तुम्ही या अॅपची सूचना वाचण्याची आणि पाठवण्याची क्षमता सेटिंग्ज > सूचना मध्ये कधीही व्यवस्थापित करू शकता."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"तुमच्या फोनवरील ॲप्स स्ट्रीम करा"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"तुमच्या फोनवरून अॅप्स आणि इतर सिस्टीम वैशिष्ट्ये स्ट्रीम करा"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 62b68f3..008535b 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Peranti berdekatan"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Tukar output media"</string>
<string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Pemberitahuan"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apl"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Penstriman"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Membuat dan mengurus panggilan telefon"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Baca dan tulis log panggilan telefon"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Menghantar dan melihat mesej SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Akses kenalan anda"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Akses kalendar anda"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Rakam audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Mencari, menyambung dan menentukan kedudukan relatif peranti berdekatan"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Membaca semua pemberitahuan, termasuk maklumat seperti kenalan, mesej dan foto"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Membaca semua pemberitahuan, termasuk maklumat seperti kenalan, mesej dan foto<br/>• Menghantar pemberitahuan<br/><br/>Anda boleh mengurus keupayaan apl ini untuk membaca dan menghantar pemberitahuan pada bila-bila masa dalam Tetapan > Pemberitahuan."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Strim apl telefon anda"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strim apl dan ciri sistem yang lain daripada telefon anda"</string>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index f99009c..05a3e88 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"အနီးတစ်ဝိုက်ရှိ စက်များ"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"မီဒီယာအထွက် ပြောင်းခြင်း"</string>
<string name="permission_storage" msgid="6831099350839392343">"ဓာတ်ပုံနှင့် မီဒီယာများ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"အကြောင်းကြားချက်များ"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"အက်ပ်များ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"တိုက်ရိုက်ဖွင့်ခြင်း"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်နိုင်၊ စီမံနိုင်သည်"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ဖုန်းခေါ်ဆိုမှတ်တမ်းကို ဖတ်နိုင်၊ ရေးနိုင်သည်"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS မက်ဆေ့ဂျ်များ ပို့နိုင်၊ ကြည့်နိုင်သည်"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"သင့်အဆက်အသွယ်များကို ဝင်ကြည့်နိုင်သည်"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"သင့်ပြက္ခဒိန်ကို ဝင်ကြည့်နိုင်သည်"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"အသံသွင်းနိုင်သည်"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"အနီးတစ်ဝိုက်ရှိ စက်များ၏ ဆက်စပ်နေရာကို ရှာခြင်း၊ ချိတ်ဆက်ခြင်းနှင့် သတ်မှတ်ခြင်းတို့ လုပ်နိုင်သည်"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"အဆက်အသွယ်၊ မက်ဆေ့ဂျ်နှင့် ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များအပါအဝင် အကြောင်းကြားချက်အားလုံးကို ဖတ်နိုင်သည်"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• အဆက်အသွယ်၊ မက်ဆေ့ဂျ်နှင့် ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များအပါအဝင် အကြောင်းကြားချက်အားလုံးကို ဖတ်ခြင်း<br/>• အကြောင်းကြားချက်များ ပို့ခြင်း<br/><br/>ဆက်တင်များ > အကြောင်းကြားချက်များ တွင် အကြောင်းကြားချက်များအား ဤအက်ပ်၏ ဖတ်ခွင့်နှင့် ပို့ခွင့်ကို အချိန်မရွေး စီမံနိုင်သည်။"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"သင့်ဖုန်းရှိအက်ပ်များကို တိုက်ရိုက်ဖွင့်နိုင်သည်"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"သင့်ဖုန်းမှ အက်ပ်များနှင့် အခြားစနစ်အင်္ဂါရပ်များကို တိုက်ရိုက်ဖွင့်သည်"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 7f09149..d910f2f 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Enheter i nærheten"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Bytt medieutgang"</string>
<string name="permission_storage" msgid="6831099350839392343">"Bilder og medier"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Varsler"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apper"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Strømming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Ring og administrer anrop"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Les og skriv samtalelogg"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Send og les SMS-meldinger"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Se kontaktene dine"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Åpne kalenderen din"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Ta opp lyd"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finn, koble til og fastslå den relative posisjonen til enheter i nærheten"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Les alle varsler, inkludert informasjon som kontakter, meldinger og bilder"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Les alle varsler, inkludert informasjon som kontakter, meldinger og bilder<br/>• Send varsler<br/><br/>Du kan når som helst administrere om denne appen kan lese og sende varsler, i Innstillinger > Varsler."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Strøm appene på telefonen"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strøm apper og andre systemfunksjoner fra telefonen"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index 38facb9..2001af2 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"नजिकैका डिभाइसहरू"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"मिडिया आउटपुट बदल्नुहोस्"</string>
<string name="permission_storage" msgid="6831099350839392343">"फोटो र मिडिया"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"सूचनाहरू"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"एपहरू"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"स्ट्रिमिङ"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"फोन कल गर्ने र व्यवस्थापन गर्ने"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"फोन कलको लग रिड र राइट गर्ने"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS म्यासेज पठाउने र हेर्ने"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"तपाईंका कन्ट्याक्टहरू हेर्ने"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"तपाईंको पात्रो प्रयोग गर्ने"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"अडियो रेकर्ड गर्ने"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"नजिकैका डिभाइसहरू भेट्टाउने, ती डिभाइससँग कनेक्ट गर्ने र तिनको सापेक्ष स्थिति निर्धारण गर्ने"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"कन्ट्याक्ट, म्यासेज र फोटो जस्ता जानकारीलगायतका सबै सूचनाहरू रिड गर्ने"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• कन्ट्याक्ट, म्यासेज र फोटो जस्ता जानकारीलगायतका सबै सूचनाहरू रिड गर्ने<br/>• सूचनाहरू पठाउने<br/><br/>तपाईं जुनसुकै बेला सेटिङ > सूचनाहरू खण्डमा गई यो एपलाई सूचनाहरू रिड गर्न र पठाउन दिने कि नदिने भन्ने कुरा व्यवस्थापन गर्न सक्नुहुन्छ।"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"आफ्नो फोनका एपहरू प्रयोग गर्नुहोस्"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"आफ्नो फोनबाट एप र सिस्टमका अन्य सुविधाहरू स्ट्रिम गर्नुहोस्"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 38c92e5..e8ccb58 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Apparaten in de buurt"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Media-uitvoer wijzigen"</string>
<string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Meldingen"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Bellen en gesprekken beheren"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefoongesprekslijst lezen en schrijven"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Sms-berichten sturen en bekijken"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Toegang tot je contacten"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Toegang tot je agenda"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Audio opnemen"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Apparaten in de buurt vinden, er verbinding mee maken en de relatieve positie ervan bepalen"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Alle meldingen lezen, waaronder informatie zoals contacten, berichten en foto\'s"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Alle meldingen lezen, waaronder informatie zoals contacten, berichten en foto\'s<br/>• Meldingen sturen<br/><br/>Je kunt de mogelijkheden van deze app om meldingen te lezen en te sturen, beheren wanneer je wilt via Instellingen > Meldingen."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream de apps van je telefoon"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Apps en andere systeemfuncties streamen vanaf je telefoon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 16985fa..0bb47b8 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକ"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"ମିଡିଆ ଆଉଟପୁଟ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="permission_storage" msgid="6831099350839392343">"ଫଟୋ ଏବଂ ମିଡିଆ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ଆପ୍ସ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ଷ୍ଟ୍ରିମିଂ"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ଫୋନ କଲଗୁଡ଼ିକ କରିବା ଏବଂ ସେଗୁଡ଼ିକୁ ପରିଚାଳନା କରିବା"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ଫୋନ କଲ ଲଗକୁ ପଢ଼ିବା ଏବଂ ଲେଖିବା"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ମେସେଜ ପଠାଇବା ଏବଂ ଭ୍ୟୁ କରିବା"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"ଆପଣଙ୍କ କଣ୍ଟାକ୍ଟଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"ଆପଣଙ୍କ କେଲେଣ୍ଡରକୁ ଆକ୍ସେସ କରିବା"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ଅଡିଓ ରେକର୍ଡ କରିବା"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକୁ ଖୋଜିବା, କନେକ୍ଟ କରିବା ଏବଂ ସେଗୁଡ଼ିକର ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ନିର୍ଦ୍ଧାରଣ କରିବା"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"କଣ୍ଟାକ୍ଟ, ମେସେଜ ଏବଂ ଫଟୋଗୁଡ଼ିକ ପରି ସୂଚନା ସମେତ ସମସ୍ତ ବିଜ୍ଞପ୍ତିକୁ ପଢ଼ନ୍ତୁ"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• କଣ୍ଟାକ୍ଟ, ମେସେଜ ଏବଂ ଫଟୋ ପରି ସୂଚନା ସମେତ ସମସ୍ତ ବିଜ୍ଞପ୍ତିକୁ ପଢ଼ନ୍ତୁ<br/>• ବିଜ୍ଞପ୍ତି ପଠାନ୍ତୁ<br/><br/>ଆପଣ ସେଟିଂସ > ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକରେ ଯେ କୌଣସି ସମୟରେ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଢିବା ଏବଂ ପଠାଇବା ପାଇଁ ଏହି ଆପର କ୍ଷମତାକୁ ପରିଚାଳନା କରିପାରିବେ।"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"ଆପଣଙ୍କ ଫୋନର ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରନ୍ତୁ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ଆପଣଙ୍କ ଫୋନରୁ ଆପ୍ସ ଏବଂ ଅନ୍ୟ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ ଷ୍ଟ୍ରିମ କରନ୍ତୁ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 31f9ec9..445afa3 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸ"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"ਮੀਡੀਆ ਆਊਟਪੁੱਟ ਬਦਲੋ"</string>
<string name="permission_storage" msgid="6831099350839392343">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਮੀਡੀਆ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"ਸੂਚਨਾਵਾਂ"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ਐਪਾਂ"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ਸਟ੍ਰੀਮਿੰਗ"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ਫ਼ੋਨ ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਉਨ੍ਹਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ਫ਼ੋਨ ਦੇ ਕਾਲ ਲੌਗ ਨੂੰ ਪੜ੍ਹਣ ਅਤੇ ਲਿਖਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਦੇਖਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"ਆਪਣੇ ਸੰਪਰਕਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"ਆਪਣੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ ਨੂੰ ਲੱਭਣ, ਉਨ੍ਹਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਅਤੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਪੜ੍ਹਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ, ਜਿਵੇਂ ਕਿ ਸੰਪਰਕਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਫ਼ੋਟੋਆਂ ਦੀ ਜਾਣਕਾਰੀ"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਪੜ੍ਹਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ, ਜਿਵੇਂ ਕਿ ਸੰਪਰਕਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਫ਼ੋਟੋਆਂ ਦੀ ਜਾਣਕਾਰੀ<br/>• ਸੂਚਨਾਵਾਂ ਭੇਜਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ<br/><br/>ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ > ਸੂਚਨਾਵਾਂ ਵਿੱਚ ਜਾ ਕੇ ਕਿਸੇ ਵੀ ਵੇਲੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਪੜ੍ਹਨ ਅਤੇ ਭੇਜਣ ਦੀ ਇਸ ਐਪ ਦੀ ਯੋਗਤਾ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹੋ।"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"ਆਪਣੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰੋ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ਆਪਣੇ ਫ਼ੋਨ ਤੋਂ ਐਪਾਂ ਅਤੇ ਹੋਰ ਸਿਸਟਮ ਸੰਬੰਧੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰੋ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index abffff1..aec7b68 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Urządzenia w pobliżu"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Zmień wyjście multimediów"</string>
<string name="permission_storage" msgid="6831099350839392343">"Zdjęcia i multimedia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Powiadomienia"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacje"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Strumieniowanie"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Nawiązywanie połączeń telefonicznych i zarządzanie nimi"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Odczytywanie i zapisywanie rejestru połączeń telefonicznych"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Wysyłanie i wyświetlanie SMS‑ów"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Dostęp do kontaktów"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Dostęp do kalendarza"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Nagrywanie dźwięku"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Znajdowanie urządzeń w pobliżu, określanie ich względnego położenia oraz łączenie się z nimi"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Odczytywanie wszystkich powiadomień, w tym informacji takich jak kontakty, wiadomości i zdjęcia"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Odczytywanie wszystkich powiadomień, w tym informacji takich jak kontakty, wiadomości i zdjęcia<br/>• Wysyłanie powiadomień<br/><br/>W każdej chwili możesz zmienić uprawnienia tej aplikacji do odczytywania i wysyłania powiadomień, klikając Ustawienia > Powiadomienia."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Odtwarzaj strumieniowo aplikacje z telefonu"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Aplikacje do odtwarzania strumieniowego i inne funkcje systemowe na Twoim telefonie"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index a95dcb6..ef1d6cd 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos por perto"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Mudar saída de mídia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notificações"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Fazer e gerenciar ligações telefônicas"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ler e gravar o registro de chamadas telefônicas"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar e visualizar mensagens SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Acessar seus contatos"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Acessar sua agenda"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Gravar áudio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar, determinar o posicionamento relativo e se conectar a dispositivos por perto"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ler todas as notificações, incluindo informações como contatos, mensagens e fotos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ler todas as notificações, incluindo informações como, por exemplo, contatos, mensagens e fotos<br/>• Enviar notificações<br/><br/>Você pode gerenciar a capacidade deste app de ler e enviar notificações a qualquer momento em \"Configurações\" e \"Notificações\"."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Fazer transmissão dos apps no seu smartphone"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Fazer streaming de apps e outros recursos do sistema pelo smartphone"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index a95dcb6..ef1d6cd 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivos por perto"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Mudar saída de mídia"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notificações"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Fazer e gerenciar ligações telefônicas"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ler e gravar o registro de chamadas telefônicas"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar e visualizar mensagens SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Acessar seus contatos"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Acessar sua agenda"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Gravar áudio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar, determinar o posicionamento relativo e se conectar a dispositivos por perto"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ler todas as notificações, incluindo informações como contatos, mensagens e fotos"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ler todas as notificações, incluindo informações como, por exemplo, contatos, mensagens e fotos<br/>• Enviar notificações<br/><br/>Você pode gerenciar a capacidade deste app de ler e enviar notificações a qualquer momento em \"Configurações\" e \"Notificações\"."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Fazer transmissão dos apps no seu smartphone"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Fazer streaming de apps e outros recursos do sistema pelo smartphone"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index a89da9dc..d2c3099 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Dispozitive din apropiere"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Schimbă ieșirea media"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotografii și media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Notificări"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplicații"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Să inițieze și să gestioneze apeluri telefonice"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Să citească și să scrie jurnalul de apeluri telefonice"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Să trimită și să vadă SMS-urile"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Să acceseze agenda"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Să acceseze calendarul"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Să înregistreze conținut audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Să găsească, să se conecteze la dispozitivele apropiate și să determine poziția relativă a acestora"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile<br/>• Să trimită notificări<br/><br/>Poți să gestionezi oricând permisiunea acestei aplicații de a citi și trimite notificări în Setări > Notificări."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Să redea în stream aplicațiile telefonului"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Redă în stream conținut din aplicații și alte funcții de sistem de pe telefon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 9cd823f..77a2c465 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Устройства поблизости"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Смена источника вывода медиа"</string>
<string name="permission_storage" msgid="6831099350839392343">"Фотографии и медиафайлы"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Уведомления"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Приложения"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Потоковая передача"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Совершение телефонных звонков и управление ими"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Просмотр списка телефонных вызовов и создание записей в нем"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Отправка и просмотр SMS-сообщений"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Доступ к контактам"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Доступ к календарю"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Запись аудио"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Поиск устройств поблизости, подключение к ним и определение их относительного местоположения"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Чтение всех уведомлений, в том числе сведений о контактах, сообщениях и фотографиях"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Чтение всех уведомлений, в том числе сведений о контактах, сообщениях и фотографиях<br/>• Отправка уведомлений<br/><br/>В разделе \"Настройки > Уведомления\" вы можете в любое время разрешить или запретить этому приложению читать и отправлять уведомления."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Трансляция приложений с телефона."</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Трансляция приложений и системных функций с телефона"</string>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index d83c1f1..1ba83fd 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"අවට උපාංග"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"මාධ්ය ප්රතිදානය වෙනස් කරන්න"</string>
<string name="permission_storage" msgid="6831099350839392343">"ඡායාරූප සහ මාධ්ය"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"දැනුම්දීම්"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"යෙදුම්"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ප්රවාහ කිරීම"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"දුරකථන ඇමතුම් ගැනීම සහ කළමනාකරණය කිරීම"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"දුරකථන ඇමතුම් ලොගය කියවන්න සහ ලියන්න"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS පණිවිඩ යැවීම සහ බැලීම"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"ඔබේ සම්බන්ධතා වෙත ප්රවේශ වන්න"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"ඔබේ දින දර්ශනයට ප්රවේශ වන්න"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ශ්රව්ය පටිගත කරන්න"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"අවට උපාංගවල සාපේක්ෂ පිහිටීම සොයා ගන්න, සම්බන්ධ වන්න, සහ තීරණය කරන්න"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"සම්බන්ධතා, පණිවිඩ, සහ ඡායාරූප වැනි තොරතුරු ඇතුළුව, සියලු දැනුම්දීම් කියවන්න"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• සම්බන්ධතා, පණිවිඩ, සහ ඡායාරූප වැනි තතු ඇතුළුව, සියලු දැනුම්දීම් කියවන්න<br/>• දැනුම්දීම් යවන්න<br/><br/>ඔබට සැකසීම් > දැනුම්දීම් තුළ ඕනෑම වේලාවක මෙම යෙදුමට දැනුම්දීම් කියවීමට සහ යැවීමට ඇති හැකියාව කළමනාකරණය කළ හැක."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"ඔබේ දුරකථනයේ යෙදුම් ප්රවාහ කරන්න"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ඔබේ දුරකථනයෙන් යෙදුම් සහ අනෙකුත් පද්ධති විශේෂාංග ප්රවාහ කරන්න"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 55e6daf8..637a240 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Zariadenia v okolí"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Zmena výstupu médií"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotky a médiá"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Upozornenia"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikácie"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Volanie a správa telefonických hovorov"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čítanie a zapisovanie do zoznamu telefonických hovorov"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Posielanie a zobrazovanie správ SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Prístup ku kontaktom"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Prístup ku kalendáru"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Nahrávanie zvuku"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vyhľadajte zariadenia v okolí, pripojte sa k nim a určite ich relatívnu polohu"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čítajte všetky upozornenia vrátane informácií, ako sú kontakty, správy a fotky"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čítajte všetky upozornenia vrátane informácií, ako sú kontakty, správy a fotky<br/>• Odosielajte upozornenia<br/><br/>Schopnosť tejto aplikácie čítať a odosielať upozornenia môžete kedykoľvek spravovať v sekcii Nastavenia > Upozornenia."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Streamovať aplikácie telefónu"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streaming aplikácii a ďalších systémových funkcií zo zariadenia"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 097b7b6..b03c27a 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Naprave v bližini"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Spreminjanje izhoda za predstavnost"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotografije in predstavnost"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Obvestila"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Pretočno predvajanje"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Opravljanje in upravljanje telefonskih klicev"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Branje in zapisovanje dnevnika klicev v telefonu"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Pošiljanje in ogled sporočil SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Dostop do stikov"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Dostop do koledarja"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Snemanje zvoka"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Iskanje naprav v bližini, povezovanje z njimi in določanje njihovega relativnega položaja"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Branje vseh obvestil, vključno s podatki, kot so stiki, sporočila in fotografije"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Branje vseh obvestil, vključno s podatki, kot so stiki, sporočila in fotografije<br/>• Pošiljanje obvestil<br/><br/>Zmožnost branja in pošiljanja obvestil za to aplikacijo lahko kadar koli upravljate v meniju »Nastavitve« > »Obvestila«."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Pretočno predvajanje aplikacij telefona"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Pretočno predvajanje aplikacij in drugih sistemskih funkcij iz telefona"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 416de58..5eeeeb8 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"Mikrofoni"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Evidencat e telefonatave"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Pajisjet në afërsi"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Të ndryshojë daljen e medias"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotografitë dhe media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Njoftimet"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacionet"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Transmetimi"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Të kryejë dhe të menaxhojë telefonatat"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Të lexojë dhe shkruajë në evidencën e telefonatave"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Të dërgojë dhe të shikojë mesazhet SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Të qaset te kontaktet e tua"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Të qaset te kalendari yt"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Të regjistrojë audion"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Të gjejë, të lidhet dhe të përcaktojë pozicionin e përafërt të pajisjeve në afërsi"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Të lexojë të gjitha njoftimet, duke përfshirë informacione si kontaktet, mesazhet dhe fotografitë"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Të lexojë të gjitha njoftimet, duke përfshirë informacione si kontaktet, mesazhet dhe fotografitë<br/>• Të dërgojë njoftime<br/><br/>Mund ta menaxhosh aftësinë e këtij aplikacioni që të lexojë dhe të dërgojë njoftime në çdo kohë te Cilësimet > Njoftimet."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Transmeto aplikacionet e telefonit tënd"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Transmeto aplikacionet dhe veçoritë e tjera të sistemit nga telefoni yt"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Të qaset te një listë e pajisjeve të disponueshme dhe të kontrollojë se cila transmeton audion ose videon nga aplikacionet e tjera"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 9b95769..2271092 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Уређаји у близини"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Промена медијског излаза"</string>
<string name="permission_storage" msgid="6831099350839392343">"Слике и медији"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Обавештења"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Апликације"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Стриминг"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Упућивање телефонских позива и управљање њима"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Читање и писање евиденције позива на телефону"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Слање и преглед SMS порука"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Приступ контактима"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Приступ календару"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Снимање звука"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Проналажење уређаја у близини, утврђивање њихове релативне позиције и повезивање са њима"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Читање свих обавештења, укључујући информација попут контаката, порука и слика"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Читање свих обавештења, укључујући информација попут контаката, порука и слика<br/>• Слање обавештења<br/><br/>Да бисте управљали дозволама ове апликације за читање и слање обавештења, идите у Подешавања > Обавештења."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Стримујте апликације на телефону"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Стримујте апликације и друге системске функције са телефона"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index e17fb04..a893476 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Enheter i närheten"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Ändra uppspelning av media"</string>
<string name="permission_storage" msgid="6831099350839392343">"Foton och media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Aviseringar"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Appar"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Ringa och hantera telefonsamtal"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Läsa och skriva samtalshistorik"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Skicka och se sms"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Få tillgång till dina kontakter"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Få tillgång till din kalender"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Spela in ljud"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Hitta, ansluta till och avgöra den relativa positionen för enheter i närheten"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Läsa alla aviseringar, inklusive information som kontakter, meddelanden och foton"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Läsa alla aviseringar, inklusive sådant som kontakter, meddelanden och foton<br/>• Skicka aviseringar<br/><br/>Du kan hantera appens möjlighet att läsa och skicka aviseringar när du vill i Inställningar > Aviseringar."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Streama telefonens appar"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streama appar och andra systemfunktioner från din telefon"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 59a6a0e..c35c407 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"Maikrofoni"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Rekodi za namba za simu"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Vifaa vilivyo karibu"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Badilisha tokeo la maudhui"</string>
<string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Arifa"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Programu"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Kutiririsha maudhui"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Kupiga na kudhibiti simu"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Kusoma na kuandika rekodi ya namba za simu"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Kutuma na kuona ujumbe wa SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Kufikia anwani zako"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Kufikia kalenda yako"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Kurekodi sauti"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Kutafuta, kuunganisha na kubaini nafasi ya makadirio ya vifaa vilivyo karibu"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kusoma arifa zote, ikiwa ni pamoja na maelezo kama vile anwani, ujumbe na picha"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Soma arifa zote, ikiwa ni pamoja na maelezo kama vile anwani, ujumbe na picha<br/>• Tuma arifa<br/><br/>Unaweza kudhibiti uwezo wa programu hii wa kusoma na kutuma arifa wakati wowote katika Mipangilio > Arifa."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Tiririsha programu za simu yako"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Kutiririsha programu na vipengele vya mfumo kwenye simu yako"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Fikia orodha ya vifaa vinavyopatikana na udhibiti unavyotumia kutiririsha au kutuma maudhui ya sauti au video kutoka kwenye programu nyingine`"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"simu"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"kompyuta kibao"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 97d59fd..1efdca4 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"மைக்ரோஃபோன்"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"அழைப்புப் பதிவுகள்"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"அருகிலுள்ள சாதனங்கள்"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"மீடியா அவுட்புட்டை மாற்றுதல்"</string>
<string name="permission_storage" msgid="6831099350839392343">"படங்கள் மற்றும் மீடியா"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"அறிவிப்புகள்"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ஆப்ஸ்"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ஸ்ட்ரீமிங்"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"மொபைல் அழைப்புகளைச் செய்யலாம் நிர்வகிக்கலாம்"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"மொபைல் அழைப்புப் பதிவைப் படிக்கலாம் எழுதலாம்"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS செய்திகளை அனுப்பலாம் பார்க்கலாம்"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"தொடர்புகளை அணுகலாம்"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"கேலெண்டரை அணுகலாம்"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ஆடியோவை ரெக்கார்டு செய்யலாம்"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"அருகிலுள்ள சாதனங்களைக் கண்டறியலாம், அவற்றுடன் இணையலாம், அவற்றின் தூரத்தைத் தீர்மானிக்கலாம்"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"தொடர்புகள், மெசேஜ்கள், படங்கள் போன்ற தகவல்கள் உட்பட அனைத்து அறிவிப்புகளையும் படிக்கலாம்"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• தொடர்புகள், மெசேஜ்கள், படங்கள் போன்ற தகவல்கள் உட்பட அனைத்து அறிவிப்புகளையும் படிக்கலாம்<br/>• அறிவிப்புகளை அனுப்பலாம்<br/><br/>இந்த ஆப்ஸின், அறிவிப்புகளைப் படிக்கும் மற்றும் அனுப்பும் திறனை எப்போது வேண்டுமானாலும் அமைப்புகள் > அறிவிப்புகள் என்பதில் நிர்வகிக்கலாம்."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"உங்கள் மொபைல் ஆப்ஸை ஸ்ட்ரீம் செய்யலாம்"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"உங்கள் மொபைலில் இருந்து ஆப்ஸையும் பிற சிஸ்டம் அம்சங்களையும் ஸ்ட்ரீம் செய்யலாம்"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"கிடைக்கக்கூடிய சாதனங்களின் பட்டியலை அணுகி, அவற்றில் எது பிற ஆப்ஸின் ஆடியோ/வீடியோவைப் பிளே செய்யலாம் அல்லது அலைபரப்பலாம் என்பதைக் கட்டுப்படுத்தும்"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"மொபைல்"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"டேப்லெட்"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index c6af6ed..3dd183e 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"సమీపంలోని పరికరాలు"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"మీడియా అవుట్పుట్ను మార్చండి"</string>
<string name="permission_storage" msgid="6831099350839392343">"ఫోటోలు, మీడియా"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"నోటిఫికేషన్లు"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"యాప్లు"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"స్ట్రీమింగ్"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"ఫోన్ కాల్స్ను చేయగలదు, మేనేజ్ చేయగలదు"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"ఫోన్ కాల్ లాగ్ను చదవగలదు, రాయగలదు"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS మెసేజ్లను పంపగలదు, చూడగలదు"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"మీ కాంటాక్ట్లను యాక్సెస్ చేయగలదు"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"మీ క్యాలెండర్ను యాక్సెస్ చేయగలదు"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"ఆడియోను రికార్డ్ చేయగలదు"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"సమీపంలోని పరికరాలను కనుగొనగలదు, వాటికి కనెక్ట్ అవ్వగలదు, అవి ఎంత దూరంలో ఉన్నాయో తెలుసుకోగలదు"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"కాంటాక్ట్లు, మెసేజ్లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్లను చదవగలదు"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• కాంటాక్ట్లు, మెసేజ్లు, ఫోటోల వంటి సమాచారంతో సహా నోటిఫికేషన్లన్నింటిని చదవగలదు<br/>• నోటిఫికేషన్లను పంపగలదు<br/><br/>నోటిఫికేషన్లను చదవగల, పంపగల ఈ యాప్ సామర్థ్యాన్ని మీరు సెట్టింగ్లు > నోటిఫికేషన్లలో ఎప్పుడైనా మేనేజ్ చేయవచ్చు."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"మీ ఫోన్లోని యాప్లను స్ట్రీమ్ చేయండి"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"మీ ఫోన్ నుండి యాప్లను, ఇతర సిస్టమ్ ఫీచర్లను స్ట్రీమ్ చేస్తుంది"</string>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index 3925747..1ed5abf 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"อุปกรณ์ที่อยู่ใกล้เคียง"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"เปลี่ยนเอาต์พุตสื่อ"</string>
<string name="permission_storage" msgid="6831099350839392343">"รูปภาพและสื่อ"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"การแจ้งเตือน"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"แอป"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"สตรีมมิง"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"โทรและจัดการการโทร"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"อ่านและเขียนบันทึกการโทรของโทรศัพท์"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"ส่งและดูข้อความ SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"เข้าถึงรายชื่อติดต่อ"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"เข้าถึงปฏิทิน"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"บันทึกเสียง"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ค้นหา เชื่อมต่อ และระบุตำแหน่งซึ่งสัมพันธ์กับอุปกรณ์ที่อยู่ใกล้เคียง"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"อ่านการแจ้งเตือนทั้งหมด รวมถึงข้อมูลอย่างรายชื่อติดต่อ ข้อความ และรูปภาพ"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• อ่านการแจ้งเตือนทั้งหมด รวมถึงข้อมูลอย่างรายชื่อติดต่อ ข้อความ และรูปภาพ<br/>• ส่งการแจ้งเตือน<br/><br/>คุณจัดการความสามารถในการอ่านและส่งการแจ้งเตือนของแอปนี้ได้ทุกเมื่อในการตั้งค่า > การแจ้งเตือน"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"สตรีมแอปของโทรศัพท์คุณ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"สตรีมแอปและฟีเจอร์อื่นๆ ของระบบจากโทรศัพท์"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index f467770..1b82d91 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Mga kalapit na device"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Palitan ang media output"</string>
<string name="permission_storage" msgid="6831099350839392343">"Mga larawan at media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Mga Notification"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Mga App"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Tumawag at mamahala ng mga tawag sa telepono"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"I-read at i-write ang log ng tawag sa telepono"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Magpadala at tumingin ng mga mensaheng SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"I-access ang iyong mga contact"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"I-access ang iyong kalendaryo"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Mag-record ng audio"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Maghanap ng, kumonekta sa, at tukuyin ang relatibong posisyon ng mga kalapit na device"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Basahin ang lahat ng notification, kabilang ang impormasyon tulad ng mga contact, mensahe, at larawan"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Basahin ang lahat ng notification, kabilang ang impormasyon tulad ng mga contact, mensahe, at larawan<br/>• Magpadala ng mga notification<br/><br/>Puwede mong pamahalaan ang kakayahan ng app na ito na magbasa at magpadala ng mga notification kahit kailan sa Mga Setting > Mga Notification."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"I-stream ang mga app ng iyong telepono"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Mag-stream ng mga app at iba pang feature ng system mula sa iyong telepono"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index cc8e898..392e158 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Yakındaki cihazlar"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Medya çıkışını değiştirin"</string>
<string name="permission_storage" msgid="6831099350839392343">"Fotoğraflar ve medya"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Bildirimler"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Uygulamalar"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Yayınlama"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon aramaları yapma ve yönetme"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon arama kaydını okuma ve yazma"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mesajları gönderme ve görüntüleme"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Kişilerinize erişme"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Takviminize erişme"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Ses kaydetme"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Yakındaki cihazları keşfedip bağlanma ve bu cihazların göreli konumunu belirleme"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kişiler, mesajlar ve fotoğraflar da dahil olmak üzere tüm bildirimleri okuma"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kişiler, mesajlar ve fotoğraflar gibi bilgiler dahil tüm bildirimleri okuma<br/>• Bildirim gönderme<br/><br/>Dilediğiniz zaman bu uygulamanın bildirim okuma ve gönderme iznini Ayarlar > Bildirimler bölümünden yönetebilirsiniz."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefonunuzun uygulamalarını yayınlayabilir"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefonunuzdan uygulamaları ve diğer sistem özelliklerini yayınlayabilir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index f0433ae..9f88d9f 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"Мікрофон"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Журнали викликів"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Пристрої поблизу"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Змін. пристр. для відтв. медіа"</string>
<string name="permission_storage" msgid="6831099350839392343">"Фотографії та медіафайли"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Сповіщення"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Додатки"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Потокове передавання"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Телефонувати й керувати дзвінками"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Переглядати й записувати дані в журналі викликів телефона"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Надсилати й переглядати SMS-повідомлення"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Отримувати доступ до контактів"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Отримувати доступ до календаря"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Записувати аудіо"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Знаходити пристрої поблизу, підключатися до них і визначати їх відносне розташування"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Читати всі сповіщення, зокрема таку інформацію, як контакти, повідомлення й фотографії"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Читати всі сповіщення, зокрема таку інформацію, як контакти, повідомлення й фотографії<br/>• Надсилати сповіщення<br/><br/>Ви можете будь-коли змінити дозвіл цього додатка на перегляд і надсилання сповіщень, вибравши \"Налаштування\" > \"Сповіщення\"."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Транслювати додатки телефона"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Транслюйте додатки й інші системні функції зі свого телефона"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Отримувати доступ до списку доступних пристроїв і вибирати, з якого з них транслюватиметься аудіо або відео з інших додатків"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"телефоні"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"планшеті"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 58c1ab6..36f8b4f 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"قریبی آلات"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"میڈیا آؤٹ پٹ کو تبدیل کریں"</string>
<string name="permission_storage" msgid="6831099350839392343">"تصاویر اور میڈیا"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"اطلاعات"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"ایپس"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"سلسلہ بندی"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"فون کالز کریں اور ان کا نظم کریں"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"فون کال لاگز پڑھیں اور لکھیں"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS پیغامات بھیجیں اور دیکھیں"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"اپنے رابطوں تک رسائی حاصل کریں"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"اپنے کیلنڈر تک رسائی حاصل کریں"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"آڈیو ریکارڈ کریں"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"قریبی آلات کی متعلقہ پوزیشن تلاش کریں، ان سے منسلک کریں اور اس کا تعین کریں"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"رابطوں، پیغامات اور تصاویر جیسی معلومات سمیت تمام اطلاعات پڑھیں"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• تمام اطلاعات کو پڑھیں، بشمول رابطے، پیغامات اور تصاویر<br/>• اطلاعات بھیجیں<br/><br/>آپ ترتیبات > اطلاعات میں کسی بھی وقت اس ایپ کی اطلاعات کو پڑھنے اور بھیجنے کی اہلیت کا نظم کر سکتے ہیں۔"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"اپنے فون کی ایپس کی سلسلہ بندی کریں"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"اپنے فون سے ایپس اور سسٹم کی دیگر خصوصیات کی سلسلہ بندی کریں"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 16a7d1d..8defcbc 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Atrofdagi qurilmalar"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Media chiqishini tanlash"</string>
<string name="permission_storage" msgid="6831099350839392343">"Suratlar va media"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Bildirishnomalar"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Ilovalar"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striming"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon qilish va chaqiruvlarni boshqarish"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon chaqiruvlari jurnalini koʻrish va oʻzgartirish"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"SMS yuborish va ularni oʻqish"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Kontaktlar bilan ishlash"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Taqvim bilan ishlash"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Audio yozib olish"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Atrofdagi qurilmalarni qidirish, joylashuvini aniqlash va ularga ulanish"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Barcha bildirishnomalarni, jumladan, kontaktlar, xabarlar va suratlarni oʻqish"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Barcha bildirishnomalarni, jumladan, kontaktlar, xabarlar va suratlarni oʻqish<br/>• Bildirishnoma yuborish<br/><br/>Ilovaning bildirishnomalarga ruxsatini istalgan vaqt Sozlamalar > Bildirishnomalar orqali boshqarish mumkin."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefondagi ilovalarni translatsiya qilish"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefoningizdan ilovalar va tizim funksiyalarini translatsiya qilish"</string>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index a080a5c..7797008 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -54,36 +54,24 @@
<string name="permission_microphone" msgid="2152206421428732949">"Micrô"</string>
<string name="permission_call_logs" msgid="5546761417694586041">"Nhật ký cuộc gọi"</string>
<string name="permission_nearby_devices" msgid="7530973297737123481">"Thiết bị ở gần"</string>
- <!-- no translation found for permission_media_routing_control (5498639511586715253) -->
- <skip />
+ <string name="permission_media_routing_control" msgid="5498639511586715253">"Thay đổi đầu ra đa phương tiện"</string>
<string name="permission_storage" msgid="6831099350839392343">"Ảnh và nội dung nghe nhìn"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Thông báo"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Ứng dụng"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Truyền trực tuyến"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Gọi và quản lý cuộc gọi điện thoại"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Đọc và ghi nhật ký cuộc gọi điện thoại"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Gửi và xem tin nhắn SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Truy cập vào danh bạ của bạn"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Truy cập lịch của bạn"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Ghi âm"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Tìm, kết nối và xác định vị trí tương đối của các thiết bị ở gần"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Đọc tất cả thông báo, kể cả những thông tin như danh bạ, tin nhắn và ảnh"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Đọc tất cả thông báo, kể cả những thông tin như danh bạ, tin nhắn và ảnh<br/>• Gửi thông báo<br/><br/>Bạn có thể quản lý tính năng đọc và gửi thông báo của ứng dụng này bất cứ lúc nào trong phần Cài đặt > Thông báo."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Truyền các ứng dụng trên điện thoại của bạn"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Truyền trực tuyến ứng dụng và các tính năng khác của hệ thống từ điện thoại của bạn"</string>
- <!-- no translation found for permission_media_routing_control_summary (2714631092321412250) -->
- <skip />
+ <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Truy cập danh sách thiết bị hiện có và kiểm soát việc thiết bị nào sẽ phát trực tuyến hoặc truyền âm thanh hoặc video từ các ứng dụng khác"</string>
<string name="device_type" product="default" msgid="8268703872070046263">"điện thoại"</string>
<string name="device_type" product="tablet" msgid="5038791954983067774">"máy tính bảng"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index aaa5bef..11e3332 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"附近的设备"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"更改媒体输出"</string>
<string name="permission_storage" msgid="6831099350839392343">"照片和媒体内容"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"应用"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"流式传输"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"拨打电话和管理通话"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"读取和写入手机通话记录"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"发送和查看短信"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"访问您的通讯录"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"访问您的日历"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"录音"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"查找、连接附近的设备以及确定附近设备的相对位置"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"读取所有通知,包括通讯录、消息和照片等信息"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• 读取所有通知,包括通讯录、消息和照片等信息<br/>• 发送通知<br/><br/>您随时可以依次前往“设置”>“通知”并在其中管理此应用读取和发送通知的权限。"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"流式传输手机上的应用"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"从您的手机流式传输应用和其他系统功能"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 7c28623..e340d5a 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"附近的裝置"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"變更媒體輸出"</string>
<string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"應用程式"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"串流"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"撥打及管理通話"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"讀取及寫入手機通話記錄"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"傳送和查看短訊"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"存取你的通訊錄"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"存取你的日曆"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"錄音"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"尋找、連接及判斷附近裝置的相對位置"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"讀取所有通知,包括聯絡人、訊息和相片等資訊"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• 讀取所有通知,包括聯絡人、訊息和相片等資訊<br/>• 傳送通知<br/><br/>你隨時可前往 [設定] > [通知],管理此應用程式讀取和傳送通知的功能。"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"串流播放手機應用程式內容"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"串流播放手機中的應用程式和其他系統功能"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index fa2d60c..82ad021 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"鄰近裝置"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"變更媒體輸出"</string>
<string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"應用程式"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"串流"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"撥打電話及管理通話"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"讀取及寫入通話記錄"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"傳送及查看簡訊"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"存取你的聯絡人"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"存取你的日曆"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"錄音"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"尋找、連線及判斷鄰近裝置的相對位置"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"讀取所有通知,包括聯絡人、訊息和相片等資訊"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• 讀取所有通知,包括聯絡人、訊息和相片等資訊<br/>• 傳送通知<br/><br/>你隨時可以前往「設定」>「通知」,管理這個應用程式讀取和傳送通知的功能。"</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"串流傳輸手機應用程式內容"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"串流播放手機中的應用程式和其他系統功能"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 2d90872..c35ff6a 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -56,28 +56,18 @@
<string name="permission_nearby_devices" msgid="7530973297737123481">"Amadivayisi aseduze"</string>
<string name="permission_media_routing_control" msgid="5498639511586715253">"Shintsha umphumela wemidiya"</string>
<string name="permission_storage" msgid="6831099350839392343">"Izithombe nemidiya"</string>
- <!-- no translation found for permission_notifications (4099418516590632909) -->
- <skip />
+ <string name="permission_notifications" msgid="4099418516590632909">"Izaziso"</string>
<string name="permission_app_streaming" msgid="6009695219091526422">"Ama-app"</string>
<string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Iyasakaza"</string>
- <!-- no translation found for permission_phone_summary (8246321093970051702) -->
- <skip />
- <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
- <skip />
- <!-- no translation found for permission_sms_summary (8499509535410068616) -->
- <skip />
- <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
- <skip />
- <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
- <skip />
- <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
- <skip />
- <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
- <skip />
- <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
- <skip />
- <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
- <skip />
+ <string name="permission_phone_summary" msgid="8246321093970051702">"Yenza futhi ulawule amakholi efoni"</string>
+ <string name="permission_call_logs_summary" msgid="7545243592757693321">"Funda futhi ubhale irekhodi lamakholi efoni"</string>
+ <string name="permission_sms_summary" msgid="8499509535410068616">"Thumela futhi ubuke imiyalezo ye-SMS"</string>
+ <string name="permission_contacts_summary" msgid="2840800622763086808">"Finyelela koxhumana nabo"</string>
+ <string name="permission_calendar_summary" msgid="8430353935747336165">"Finyelela ikhalenda lakho"</string>
+ <string name="permission_microphone_summary" msgid="4862628553869973259">"Rekhoda umsindo"</string>
+ <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Thola, uxhume, futhi unqume indawo yamadivayisi aseduze"</string>
+ <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Funda zonke izaziso, okuhlanganisa ulwazi loxhumana nabo, imiyalezo, nezithombe"</string>
+ <string name="permission_notifications_summary" msgid="2272810466047367030">"• Funda zonke izaziso, okuhlanganisa ulwazi loxhumana nabo, imiyalezo, nezithombe<br/>•Thumela izaziso<br/><br/>Ungakwazi ukulawula ikhono lale app lokufunda nokuthumela izaziso noma nini kokuthi Amasethingi Nezaziso."</string>
<string name="permission_app_streaming_summary" msgid="606923325679670624">"Sakaza ama-app wefoni yakho"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Sakaza ama-app nezinye izakhi zesistimu kusuka kufoni yakho"</string>
diff --git a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml
new file mode 100644
index 0000000..9d16f32d
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<!-- Copied from //frameworks/base/core/res/res/drawable/item_background_material.xml -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/autofill_light_colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <color android:color="@android:color/white"/>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
new file mode 100644
index 0000000..2f0c83b
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi"
+ android:color="@android:color/transparent">
+ <item
+ android:bottom="1dp"
+ android:shape="rectangle"
+ android:top="1dp">
+ <shape>
+ <corners android:radius="28dp" />
+ <solid android:color="@android:color/system_surface_container_high_light" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml
new file mode 100644
index 0000000..39f49ca
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi"
+ android:color="@android:color/transparent">
+ <item
+ android:bottom="1dp"
+ android:shape="rectangle"
+ android:top="1dp">
+ <shape>
+ <corners android:radius="28dp" />
+ <solid android:color="@android:color/system_surface_container_high_dark" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml
new file mode 100644
index 0000000..e4e9f7a
--- /dev/null
+++ b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml
@@ -0,0 +1,37 @@
+<!--
+ ~ 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.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/autofill.Dataset">
+ <ImageView
+ android:id="@android:id/icon1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentStart="true"
+ android:background="@null"/>
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@android:id/icon1"
+ style="@style/autofill.TextAppearance"/>
+
+</RelativeLayout>
diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml
new file mode 100644
index 0000000..63b9f24
--- /dev/null
+++ b/packages/CredentialManager/res/values/colors.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ 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.
+ -->
+
+<!-- Color palette -->
+<resources>
+ <color name="autofill_light_colorPrimary">@color/primary_material_light</color>
+ <color name="autofill_light_colorAccent">@color/accent_material_light</color>
+ <color name="autofill_light_colorControlHighlight">@color/ripple_material_light</color>
+ <color name="autofill_light_colorButtonNormal">@color/button_material_light</color>
+
+ <!-- Text colors -->
+ <color name="autofill_light_textColorPrimary">@color/abc_primary_text_material_light</color>
+ <color name="autofill_light_textColorSecondary">@color/abc_secondary_text_material_light</color>
+ <color name="autofill_light_textColorHint">@color/abc_hint_foreground_material_light</color>
+ <color name="autofill_light_textColorHintInverse">@color/abc_hint_foreground_material_dark
+ </color>
+ <color name="autofill_light_textColorHighlight">@color/highlighted_text_material_light</color>
+ <color name="autofill_light_textColorLink">@color/autofill_light_colorAccent</color>
+
+ <!-- These colors are used for Remote Views. -->
+ <color name="background_dark_mode">#0E0C0B</color>
+ <color name="background">#F1F3F4</color>
+ <color name="text_primary_dark_mode">#DFDEDB</color>
+ <color name="text_primary">#202124</color>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml
new file mode 100644
index 0000000..67003a3
--- /dev/null
+++ b/packages/CredentialManager/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <dimen name="autofill_view_padding">16dp</dimen>
+ <dimen name="autofill_icon_size">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/styles.xml b/packages/CredentialManager/res/values/styles.xml
new file mode 100644
index 0000000..4a5761a
--- /dev/null
+++ b/packages/CredentialManager/res/values/styles.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <style name="autofill.TextAppearance.Small" parent="@style/autofill.TextAppearance">
+ <item name="android:textSize">12sp</item>
+ </style>
+
+
+ <style name="autofill.Dataset" parent="">
+ <item name="android:background">@drawable/autofill_light_selectable_item_background</item>
+ </style>
+
+ <style name="autofill.TextAppearance" parent="">
+ <item name="android:textColor">@color/autofill_light_textColorPrimary</item>
+ <item name="android:textColorHint">@color/autofill_light_textColorHint</item>
+ <item name="android:textColorHighlight">@color/autofill_light_textColorHighlight</item>
+ <item name="android:textColorLink">@color/autofill_light_textColorLink</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="autofill.TextAppearance.Primary">
+ <item name="android:textColor">@color/autofill_light_textColorPrimary</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 20d2f09..0ff1c7f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager.autofill
+import android.R
import android.app.assist.AssistStructure
import android.content.Context
import android.credentials.CredentialManager
@@ -41,18 +42,19 @@
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.view.autofill.AutofillId
-import org.json.JSONException
import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
import androidx.credentials.provider.CustomCredentialEntry
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.PublicKeyCredentialEntry
import com.android.credentialmanager.GetFlowUtils
-import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.common.ui.RemoteViewsFactory
import com.android.credentialmanager.getflow.ProviderDisplayInfo
-import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.getflow.toProviderDisplayInfo
import com.android.credentialmanager.ktx.credentialEntry
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.ProviderInfo
+import org.json.JSONException
import org.json.JSONObject
import java.util.concurrent.Executors
@@ -127,9 +129,11 @@
is PasswordCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
+
is PublicKeyCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
+
is CustomCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
@@ -172,11 +176,11 @@
}
private fun processProvidersForAutofillId(
- filLRequest: FillRequest,
- autofillId: AutofillId,
- providerList: List<ProviderInfo>,
- entryIconMap: Map<String, Icon>,
- fillResponseBuilder: FillResponse.Builder
+ filLRequest: FillRequest,
+ autofillId: AutofillId,
+ providerList: List<ProviderInfo>,
+ entryIconMap: Map<String, Icon>,
+ fillResponseBuilder: FillResponse.Builder
): Boolean {
if (providerList.isEmpty()) {
return false
@@ -197,7 +201,7 @@
var i = 0
var datasetAdded = false
- providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
+ providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{
val primaryEntry = it.sortedCredentialEntryList.first()
val pendingIntent = primaryEntry.pendingIntent
val fillInIntent = primaryEntry.fillInIntent
@@ -206,37 +210,48 @@
Log.e(TAG, "PendingIntent was missing from the entry.")
return@usernameLoop
}
- if (inlinePresentationSpecs == null || i >= maxItemCount) {
+ if (inlinePresentationSpecs == null) {
+ Log.i(TAG, "Inline presentation spec is null, " +
+ "building dropdown presentation only")
+ }
+ if (i >= maxItemCount) {
Log.e(TAG, "Skipping because reached the max item count.")
return@usernameLoop
}
- // Create inline presentation
- val spec: InlinePresentationSpec
- if (i < inlinePresentationSpecsCount) {
- spec = inlinePresentationSpecs[i]
- } else {
- spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
- }
- val sliceBuilder = InlineSuggestionUi
- .newContentBuilder(pendingIntent)
- .setTitle(primaryEntry.userName)
- val icon: Icon
- if (primaryEntry.icon == null) {
+ val icon: Icon = if (primaryEntry.icon == null) {
// The empty entry icon has non-null icon reference but null drawable reference.
// If the drawable reference is null, then use the default icon.
- icon = getDefaultIcon()
+ getDefaultIcon()
} else {
- icon = entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey]
+ entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey]
?: getDefaultIcon()
}
- sliceBuilder.setStartIcon(icon)
- val inlinePresentation = InlinePresentation(
- sliceBuilder.build().slice, spec, /* pinned= */ false)
+ // Create inline presentation
+ var inlinePresentation: InlinePresentation? = null;
+ if (inlinePresentationSpecs != null) {
+ val spec: InlinePresentationSpec
+ if (i < inlinePresentationSpecsCount) {
+ spec = inlinePresentationSpecs[i]
+ } else {
+ spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+ }
+ val sliceBuilder = InlineSuggestionUi
+ .newContentBuilder(pendingIntent)
+ .setTitle(primaryEntry.userName)
+ sliceBuilder.setStartIcon(icon)
+ inlinePresentation = InlinePresentation(
+ sliceBuilder.build().slice, spec, /* pinned= */ false)
+ }
+ val dropdownPresentation = RemoteViewsFactory.createDropdownPresentation(
+ this, icon, primaryEntry)
i++
val dataSetBuilder = Dataset.Builder()
val presentationBuilder = Presentations.Builder()
- .setInlinePresentation(inlinePresentation)
+ .setMenuPresentation(dropdownPresentation)
+ if (inlinePresentation != null) {
+ presentationBuilder.setInlinePresentation(inlinePresentation)
+ }
fillResponseBuilder.addDataset(
dataSetBuilder
@@ -305,7 +320,7 @@
): MutableMap<AutofillId, MutableList<CredentialEntryInfo>> {
val autofillIdToCredentialEntries:
MutableMap<AutofillId, MutableList<CredentialEntryInfo>> = mutableMapOf()
- credentialEntryList.forEach entryLoop@ { credentialEntry ->
+ credentialEntryList.forEach entryLoop@{ credentialEntry ->
val autofillId: AutofillId? = credentialEntry
.fillInIntent
?.getParcelableExtra(
@@ -323,8 +338,8 @@
}
private fun copyProviderInfo(
- providerInfo: ProviderInfo,
- credentialList: List<CredentialEntryInfo>
+ providerInfo: ProviderInfo,
+ credentialList: List<CredentialEntryInfo>
): ProviderInfo {
return ProviderInfo(
providerInfo.id,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
new file mode 100644
index 0000000..4dc7f00
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.credentialmanager.common.ui
+
+import android.content.Context
+import android.content.res.Configuration
+import android.widget.RemoteViews
+import androidx.core.content.ContextCompat
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import android.graphics.drawable.Icon
+
+class RemoteViewsFactory {
+
+ companion object {
+ private const val setAdjustViewBoundsMethodName = "setAdjustViewBounds"
+ private const val setMaxHeightMethodName = "setMaxHeight"
+ private const val setBackgroundResourceMethodName = "setBackgroundResource"
+
+ fun createDropdownPresentation(
+ context: Context,
+ icon: Icon,
+ credentialEntryInfo: CredentialEntryInfo
+ ): RemoteViews {
+ val padding = context.resources.getDimensionPixelSize(com.android
+ .credentialmanager.R.dimen.autofill_view_padding)
+ var layoutId: Int = com.android.credentialmanager.R.layout
+ .autofill_dataset_left_with_item_tag_hint
+ val remoteViews = RemoteViews(context.packageName, layoutId)
+ setRemoteViewsPaddings(remoteViews, padding)
+ val textColorPrimary = getTextColorPrimary(isDarkMode(context), context);
+ remoteViews.setTextColor(android.R.id.text1, textColorPrimary);
+ remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName)
+
+ remoteViews.setImageViewIcon(android.R.id.icon1, icon);
+ remoteViews.setBoolean(
+ android.R.id.icon1, setAdjustViewBoundsMethodName, true);
+ remoteViews.setInt(
+ android.R.id.icon1,
+ setMaxHeightMethodName,
+ context.resources.getDimensionPixelSize(
+ com.android.credentialmanager.R.dimen.autofill_icon_size));
+ val drawableId = if (isDarkMode(context))
+ com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one_dark
+ else com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one
+ remoteViews.setInt(
+ android.R.id.content, setBackgroundResourceMethodName, drawableId);
+ return remoteViews
+ }
+
+ private fun setRemoteViewsPaddings(
+ remoteViews: RemoteViews,
+ padding: Int) {
+ val halfPadding = padding / 2
+ remoteViews.setViewPadding(
+ android.R.id.text1,
+ halfPadding,
+ halfPadding,
+ halfPadding,
+ halfPadding)
+ }
+
+ private fun isDarkMode(context: Context): Boolean {
+ val currentNightMode = context.resources.configuration.uiMode and
+ Configuration.UI_MODE_NIGHT_MASK
+ return currentNightMode == Configuration.UI_MODE_NIGHT_YES
+ }
+
+ private fun getTextColorPrimary(darkMode: Boolean, context: Context): Int {
+ return if (darkMode) ContextCompat.getColor(
+ context, com.android.credentialmanager.R.color.text_primary_dark_mode)
+ else ContextCompat.getColor(context, com.android.credentialmanager.R.color.text_primary)
+ }
+ }
+}
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 933be11..6a4bb21 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -134,8 +134,7 @@
@UsesReflection({
// As the runtime class name is used to generate the returned name, and the returned
// name may be used used with reflection, generate the necessary keep rules.
- @KeepTarget(classConstant = LocalTransport.class),
- @KeepTarget(extendsClassConstant = LocalTransport.class)
+ @KeepTarget(instanceOfClassConstant = LocalTransport.class)
})
@Override
public String name() {
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 25ad9b8..98a5a67 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -35,7 +35,10 @@
name: "PackageInstaller",
defaults: ["platform_app_defaults"],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
certificate: "platform",
privileged: true,
@@ -62,7 +65,10 @@
name: "PackageInstaller_tablet",
defaults: ["platform_app_defaults"],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
certificate: "platform",
privileged: true,
@@ -91,7 +97,10 @@
name: "PackageInstaller_tv",
defaults: ["platform_app_defaults"],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
certificate: "platform",
privileged: true,
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 25afe99..acd16b9 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Hierdie gebruiker kan nie onbekende programme installeer nie"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Hierdie gebruiker word nie toegelaat om programme te installeer nie"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Argiveer"</string>
<string name="update_anyway" msgid="8792432341346261969">"Dateer in elk geval op"</string>
<string name="manage_applications" msgid="5400164782453975580">"Bestuur programme"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Geen spasie oor nie"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Deïnstalleer opdatering"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is deel van die volgende program:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie program deïnstalleer?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Jou persoonlike data sal gestoor word"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Argiveer hierdie app vir alle gebruikers? Jou persoonlike data sal gestoor word"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Argiveer hierdie app op jou werkprofiel? Jou persoonlike data sal gestoor word"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Argiveer hierdie app vir <xliff:g id="USERNAME">%1$s</xliff:g>? Jou persoonlike data sal gestoor word"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Wil jy hierdie app wat in jou privaat ruimte is, argiveer? Jou persoonlike data sal gestoor word"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Wil jy hierdie program vir "<b>"alle"</b>" gebruikers deïnstalleer? Die program en sy data sal van "<b>"alle"</b>" gebruikers op hierdie toestel verwyder word."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Wil jy hierdie program vir die gebruiker <xliff:g id="USERNAME">%1$s</xliff:g> deïnstalleer?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil jy hierdie program op jou werkprofiel deïnstalleer?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Jou tablet en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou tablet of verlies van data wat uit sy gebruik kan spruit."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Jou TV en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou TV of verlies van data wat uit sy gebruik kan spruit."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Argiveer <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Gaan voort"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Instellings"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Installeer/deïnstalleer Wear-programme"</string>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
index f47ec6a..d2e8400 100644
--- a/packages/PackageInstaller/res/values-am/strings.xml
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ያልታወቁ መተግበሪያዎች በዚህ ተጠቃሚ ሊጫኑ አይችሉም"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ይህ ተጠቃሚ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም"</string>
<string name="ok" msgid="7871959885003339302">"እሺ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"በማህደር አስቀምጥ"</string>
<string name="update_anyway" msgid="8792432341346261969">"የሆነው ሆኖ አዘምን"</string>
<string name="manage_applications" msgid="5400164782453975580">"መተግበሪያዎችን ያቀናብሩ"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ቦታ ሞልቷል"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"ዝማኔን አራግፍ"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> የሚከተለው መተግበሪያ አካል ነው፦"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ይህን መተግበሪያ ማራገፍ ይፈልጋሉ?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"የግል ውሂብዎ ይቀመጣል"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ይህ መተግበሪያ ለሁሉም ተጠቃሚዎች በማህደር ይቀመጥ? የግል ውሂብዎ ይቀመጣል"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ይህ መተግበሪያ በስራ መገለጫዎ ላይ በማህደር ይቀመጥ? የግል ውሂብዎ ይቀመጣል"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"ይህ መተግበሪያ ለ<xliff:g id="USERNAME">%1$s</xliff:g> በማህደር ይቀመጥ? የግል ውሂብዎ ይቀመጣል"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ይህን መተግበሪያ ከግል ቦታዎ በማህደር ማስቀመጥ ይፈልጋሉ? የግል ውሂብዎ ይቀመጣል"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ይህን መተግበሪያ "<b>"ለሁሉም"</b>" ተጠቃሚዎች መጫን ይፈልጋሉ? መተግበሪያው እና ውሂቡ በመሣሪያው ላይ ካሉ "<b>"ሁሉም"</b>" ተጠቃሚዎች ይሰረዛሉ።"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"ይህን መተግበሪያ ለተጠቃሚ <xliff:g id="USERNAME">%1$s</xliff:g> ማራገፍ ይፈልጋሉ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ይህን መተግበሪያ ከስራ መገለጫዎ ማራገፍ ይፈልጋሉ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"የእርስዎ ጡባዊ እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ጡባዊ ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"የእርስዎ ቴሌቪዥን እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ቴሌቪዥን ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"የተባዛ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> በማህደር ይቀመጥ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ቀጥል"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ቅንብሮች"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"የWear መተግበሪያዎችን መጫን/ማራገፍ"</string>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
index 62a55dc..8a253a3 100644
--- a/packages/PackageInstaller/res/values-as/strings.xml
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"এই ব্যৱহাৰকাৰীয়ে অজ্ঞাত উৎসৰপৰা পোৱা এপ্সমূহ ইনষ্টল কৰিব নোৱাৰে"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"এই ব্যৱহাৰকাৰীজনৰ এপ্ ইনষ্টল কৰাৰ অনুমতি নাই"</string>
<string name="ok" msgid="7871959885003339302">"ঠিক আছে"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"আৰ্কাইভ কৰক"</string>
<string name="update_anyway" msgid="8792432341346261969">"যিকোনো প্ৰকাৰে আপডে’ট কৰক"</string>
<string name="manage_applications" msgid="5400164782453975580">"এপ্ পৰিচালনা"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"খালী ঠাই নাই"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"আপডে’ট আনইনষ্টল কৰক"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> হৈছে তলৰ এপ্টোৰ এটা অংশ:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"আপুনি এই এপ্টো আনইনষ্টল কৰিব বিচাৰে নেকি?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"আপোনাৰ ব্যক্তিগত ডেটা ছেভ কৰা হ’ব"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"সকলো ব্যৱহাৰকাৰীৰ বাবে এই এপ্টো আৰ্কাইভ কৰিবনে? আপোনাৰ ব্যক্তিগত ডেটা ছেভ কৰা হ’ব"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"আপোনাৰ কৰ্মস্থানৰ প্ৰ’ফাইলত এই এপ্টো আৰ্কাইভ কৰিবনে? আপোনাৰ ব্যক্তিগত ডেটা ছেভ কৰা হ’ব"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>ৰ বাবে এই এপ্টো আৰ্কাইভ কৰিবনে? আপোনাৰ ব্যক্তিগত ডেটা ছেভ কৰা হ’ব"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"আপুনি আপোনাৰ ব্যক্তিগত স্পেচৰ পৰা এই এপ্টো আৰ্কাইভ কৰিব বিচাৰেনে? আপোনাৰ ব্যক্তিগত ডেটা ছেভ কৰা হ’ব"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"আপুনি "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ বাবে এই এপ্টো আনইনষ্টল কৰিব বিচাৰেনে? এপ্লিকেশ্বন আৰু ইয়াৰ ডেটা ডিভাইচটোত থকা "<b>"সকলো"</b>" ব্যৱহাৰকাৰীৰ পৰা আঁতৰোৱা হ\'ব৷"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"আপুনি ব্যৱহাৰকাৰীৰ <xliff:g id="USERNAME">%1$s</xliff:g> বাবে এই এপ্টো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"আপুনি নিজৰ কৰ্মস্থানৰ প্ৰ’ফাইলৰ পৰা এই এপ্টো আনইনষ্টল কৰিব বিচাৰেনে?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"আপোনাৰ টেবলেট আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। আপুনি এই এপ্টো ইনষ্টল কৰি এপ্টোৰ ব্যৱহাৰৰ ফলত আপোনাৰ টিভিত হ\'ব পৰা যিকোনো ক্ষতি বা ডেটা ক্ষয়ৰ বাবে আপুনি নিজে দায়ী হ\'ব বুলি সন্মতি দিয়ে।"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"আপোনাৰ টিভি আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। আপুনি এই এপ্টো ইনষ্টল কৰি এপ্টোৰ ব্যৱহাৰৰ ফলত আপোনাৰ টিভিত হ\'ব পৰা যিকোনো ক্ষতি বা ডেটা ক্ষয়ৰ বাবে আপুনি নিজে দায়ী হ\'ব বুলি সন্মতি দিয়ে।"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ৰ ক্ল’ন"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আৰ্কাইভ কৰিবনে?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"অব্যাহত ৰাখক"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ছেটিং"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"ৱেৰ এপ্সমূহ ইনষ্টল/আনইনষ্টল কৰি থকা হৈছে"</string>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
index f5e3974..e946326 100644
--- a/packages/PackageInstaller/res/values-az/strings.xml
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Naməlum tətbiqlər bu istifadəçi tərəfindən quraşdırıla bilməz"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Bu istifadəçinin tətbiqi quraşdırmaq üçün icazəsi yoxdur"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arxivə atın"</string>
<string name="update_anyway" msgid="8792432341346261969">"İstənilən halda güncəlləyin"</string>
<string name="manage_applications" msgid="5400164782453975580">"Tətbiqi idarə edin"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Boş yer yoxdur"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Güncəlləməni silin"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> bu tətbiqin hissəsidir:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Bu tətbiqi sistemdən silmək istəyirsiniz?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Şəxsi məlumatlarınız yadda saxlanacaq"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Bu tətbiq bütün istifadəçilər üçün arxivə atılsın? Şəxsi məlumatlarınız yadda saxlanacaq"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Bu tətbiq iş profilində arxivə atılsın? Şəxsi məlumatlarınız yadda saxlanacaq"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Bu tətbiq <xliff:g id="USERNAME">%1$s</xliff:g> üçün arxivə atılsın? Şəxsi məlumatlarınız yadda saxlanacaq"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Bu tətbiqi şəxsi sahədən arxivə atmaq istəyirsiniz? Şəxsi məlumatlarınız yadda saxlanacaq"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Bu tətbiqi "<b>"bütün"</b>" istifadəçilər üçün silmək istəyirsiz? Tətbiq və onun datası cihazdakı "<b>"bütün"</b>" istifadəçilər üçün silinəcək."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> adlı istifadəçi üçün bu tətbiqi sistemdən silmək istəyirsiniz?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Bu tətbiqi iş profilinizdən silmək istəyirsiniz?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Planşet və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla planşetə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verə biləcək data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tv və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla Tv-yə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verən data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> Kopyalayın"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> arxivə atılsın?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Davam edin"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ayarlar"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear tətbiqləri quraşdırılır/sistemdən silinir"</string>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
index 111d90b..5627324 100644
--- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ovaj korisnik ne može da instalira nepoznate aplikacije"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ovom korisniku nije dozvoljeno da instalira aplikacije"</string>
<string name="ok" msgid="7871959885003339302">"Potvrdi"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arhiviraj"</string>
<string name="update_anyway" msgid="8792432341346261969">"Ipak ažuriraj"</string>
<string name="manage_applications" msgid="5400164782453975580">"Upravljajte apl."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nema više prostora"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Deinstaliraj ažuriranje"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je deo sledeće aplikacije:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Želite da deinstalirate ovu aplikaciju?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Lični podaci će biti sačuvani"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Želite li da arhivirate ovu aplikaciju za sve korisnike? Lični podaci će biti sačuvani"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Želite li da arhivirate ovu aplikaciju sa poslovnog profila? Lični podaci će biti sačuvani"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Želite li da arhivirate ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>? Lični podaci će biti sačuvani"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Želite li da arhivirate ovu aplikaciju iz privatnog prostora? Lični podaci će biti sačuvani"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Da li želite da deinstalirate ovu aplikaciju za "<b>"sve"</b>" korisnike? Aplikacija i podaci uz nje biće uklonjeni za "<b>"sve"</b>" korisnike ovog uređaja."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Želite li da deinstalirate ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Da li želite da deinstalirate ovu aplikaciju sa poslovnog profila?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja tableta ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja TV-a ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Želite li da arhivirate <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Podešavanja"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear aplikac."</string>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index d34ecb6..dcf3ad7 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Гэты карыстальнік не можа ўсталёўваць невядомыя праграмы"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Гэты карыстальнік не можа ўсталёўваць праграмы"</string>
<string name="ok" msgid="7871959885003339302">"ОК"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Архіваваць"</string>
<string name="update_anyway" msgid="8792432341346261969">"Усё роўна абнавіць"</string>
<string name="manage_applications" msgid="5400164782453975580">"Кіраваць"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Не хапае месца"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Выдаліць абнаўленне"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> з\'яўляецца часткай наступнай праграмы:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Выдаліць гэту праграму?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Вашы асабістыя даныя будуць захаваны"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Архіваваць гэту праграму для ўсіх карыстальнікаў? Вашы асабістыя даныя будуць захаваны."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Архіваваць гэту праграму з вашага працоўнага профілю? Вашы асабістыя даныя будуць захаваны."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Архіваваць гэту праграму для карыстальніка <xliff:g id="USERNAME">%1$s</xliff:g>? Вашы асабістыя даныя будуць захаваны."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Архіваваць гэту праграму з вашай прыватнай прасторы? Вашы асабістыя даныя будуць захаваны."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Выдаліць гэту праграму для "<b>"ўсіх"</b>" карыстальнікаў? Праграма і яе даныя будуць выдалены для "<b>"ўсіх"</b>" карыстальнікаў прылады."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Хочаце выдаліць гэту праграму для карыстальніка <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Хочаце выдаліць гэту праграму з працоўнага профілю?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ваш планшэт і асабістыя даныя больш прыступныя для атак невядомых праграм. Усталёўваючы гэту праграму, вы згаджаецеся з тым, што несяце адказнасць за любыя пашкоджанні планшэта ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Ваш тэлевізар і асабістыя даныя больш прыступныя для атак невядомых праграм. Усталёўваючы гэту праграму, вы згаджаецеся з тым, што несяце адказнасць за любыя пашкоджанні тэлевізара ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Клон \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Архіваваць праграму \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\"?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Далей"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Налады"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Усталяванне і выдаленне праграм Wear"</string>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index e0cd7a0..1d16074 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Този потребител не може да инсталира неизвестни приложения"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Този потребител няма разрешение да инсталира приложения"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Архив"</string>
<string name="update_anyway" msgid="8792432341346261969">"Актуализиране въпреки това"</string>
<string name="manage_applications" msgid="5400164782453975580">"Прил.: Управл."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Няма място"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Деинст. на актуализацията"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> е част от следното приложение:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Искате ли да деинсталирате това приложение?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Личните ви данни ще бъдат запазени"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Да се архивира ли това приложение за всички потребители? Личните ви данни ще бъдат запазени"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Да се архивира ли това приложение в служебния ви потребителски профил? Личните ви данни ще бъдат запазени"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Да се архивира ли това приложение за <xliff:g id="USERNAME">%1$s</xliff:g>? Личните ви данни ще бъдат запазени"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Искате ли да архивирате това приложение от личното си пространство? Личните ви данни ще бъдат запазени"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Искате ли да деинсталирате това приложение за "<b>"всички"</b>" потребители? Приложението и данните му ще бъдат премахнати от "<b>"всички"</b>" потребители на устройството."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Искате ли да деинсталирате това приложение за потребителя <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Искате ли да деинсталирате това приложение от служебния си потребителски профил?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Таблетът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на таблета или загуба на информация вследствие на използването на приложението."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Телевизорът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на телевизора или загуба на информация вследствие на използването на приложението."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Копие на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Да се архивира ли <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Напред"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Настройки"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Инсталир./деинсталир. на прилож. за Wear"</string>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
index bab2d08..b6a36d2 100644
--- a/packages/PackageInstaller/res/values-bn/strings.xml
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"এই ব্যবহারকারী অজানা অ্যাপ ইনস্টল করতে পারেন না"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"এই ব্যবহারকারীর অ্যাপ ইনস্টল করার অনুমতি নেই"</string>
<string name="ok" msgid="7871959885003339302">"ঠিক আছে"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"আর্কাইভ করুন"</string>
<string name="update_anyway" msgid="8792432341346261969">"তবুও আপডেট করতে চাই"</string>
<string name="manage_applications" msgid="5400164782453975580">"অ্যাপ পরিচালনা"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"জায়গা খালি নেই"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"আপডেট আনইনস্টল করুন"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> এই অ্যাপটির অংশ:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"আপনি কি এই অ্যাপটি আনইনস্টল করতে চান?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"আপনার ব্যক্তিগত ডেটা সেভ করা হবে"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"সব ব্যবহারকারীর জন্য এই অ্যাপ আর্কাইভ করতে চান? আপনার ব্যক্তিগত ডেটা সেভ করা হবে"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"আপনার অফিস প্রোফাইলে এই অ্যাপ আর্কাইভ করতে চান? আপনার ব্যক্তিগত ডেটা সেভ করা হবে"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>-এর জন্য এই অ্যাপ আর্কাইভ করতে চান? আপনার ব্যক্তিগত ডেটা সেভ করা হবে"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"আপনার ব্যক্তিগত স্পেস থেকে এই অ্যাপ আর্কাইভ করতে চান? আপনার ব্যক্তিগত ডেটা সেভ করা হবে"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"আপনি কি "<b>"সব"</b>" ব্যবহারকারীর জন্য এই অ্যাপটিকে আনইনস্টল করতে চান? এই ডিভাইসের "<b>"সব"</b>" ব্যবহারকারীর ডেটা সহ এই অ্যাপ্লিকেশনটি সরিয়ে দেওয়া হবে।"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"আপনি কি <xliff:g id="USERNAME">%1$s</xliff:g>-এর জন্য এই অ্যাপটি আনইনস্টল করতে চান?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"আপনার অফিস প্রোফাইল থেকে এই অ্যাপ আনইনস্টল করতে চান?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"অজানা অ্যাপের দ্বারা আপনার ট্যাবলেট এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হচ্ছেন যে এটি ব্যবহারের ফলে আপনার ট্যাবলেটের বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"অজানা অ্যাপের দ্বারা আপনার টিভি এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হচ্ছেন যে এটি ব্যবহারের ফলে আপনার টিভি বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ক্লোন"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> আর্কাইভ করতে চান?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"চালিয়ে যান"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"সেটিংস"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear অ্যাপ ইনস্টল/আনইনস্টল করা হচ্ছে"</string>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
index defb388..9801e07 100644
--- a/packages/PackageInstaller/res/values-bs/strings.xml
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ovaj korisnik ne može instalirati nepoznate aplikacije"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ovom korisniku nije dozvoljeno instaliranje aplikacija"</string>
<string name="ok" msgid="7871959885003339302">"Uredu"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arhiviraj"</string>
<string name="update_anyway" msgid="8792432341346261969">"Ipak ažuriraj"</string>
<string name="manage_applications" msgid="5400164782453975580">"Uprav. aplik."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nedostatak prostora"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Deinstaliraj ažuriranje"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je dio sljedeće aplikacije:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Želite li deinstalirati ovu aplikaciju?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Vaši lični podaci će se sačuvati"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arhivirati ovu aplikaciju za sve korisnike? Vaši lični podaci će se sačuvati"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arhivirati ovu aplikaciju na radni profil? Vaši lični podaci će se sačuvati"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Arhivirati ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>? Vaši lični podaci će se sačuvati"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Želite li arhivirati ovu aplikaciju iz privatnog prostora? Vaši lični podaci će se sačuvati"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Želite li deinstalirati ovu aplikaciju za "<b>" sve "</b>" korisnike? Aplikacija i njeni podaci će biti uklonjeni iz "<b>" svih "</b>" korisničkih računa na uređaju."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Želite li deinstalirati ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Želite li deinstalirati ovu aplikaciju s radnog profila?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Vaši podaci na tabletu i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, prihvatate odgovornost za bilo kakvu štetu na tabletu ili gubitak podataka do kojih može doći korištenjem aplikacije."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Vaši podaci na TV-u i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, prihvatate odgovornost za bilo kakvu štetu na TV-u ili gubitak podataka do kojih može doći korištenjem aplikacije."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Arhivirati aplikaciju <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Postavke"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear aplik."</string>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index 50e105c..e4da208 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aquest usuari no pot instal·lar aplicacions desconegudes"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Aquest usuari no té permís per instal·lar aplicacions"</string>
<string name="ok" msgid="7871959885003339302">"D\'acord"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arxiva"</string>
<string name="update_anyway" msgid="8792432341346261969">"Actualitza de tota manera"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gestiona apps"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Espai esgotat"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstal·la l\'actualització"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma part de l\'aplicació següent:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Vols desinstal·lar aquesta aplicació?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Les teves dades personals es desaran"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vols arxivar aquesta aplicació per a tots els usuaris? Les teves dades personals es desaran."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vols arxivar aquesta aplicació al teu perfil de treball? Les teves dades personals es desaran."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Vols arxivar aquesta aplicació per a <xliff:g id="USERNAME">%1$s</xliff:g>? Les teves dades personals es desaran."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vols arxivar aquesta aplicació del teu espai privat? Les teves dades personals es desaran."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vols desinstal·lar aquesta aplicació per a "<b>"tots"</b>" els usuaris? L\'aplicació i les seves dades se suprimiran per a "<b>"tots"</b>" els usuaris del dispositiu."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Vols desinstal·lar aquesta aplicació per a l\'usuari <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vols desinstal·lar aquesta aplicació del teu perfil de treball?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"La tauleta i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi a la tauleta o de la pèrdua de dades que pugui resultar del seu ús."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"El televisor i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al televisor o de la pèrdua de dades que pugui resultar del seu ús."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Vols arxivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continua"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Configuració"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instal·lant o desinstal·lant apps de Wear"</string>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
index 3b204be..5bdc12d 100644
--- a/packages/PackageInstaller/res/values-cs/strings.xml
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Tento uživatel nemůže instalovat neznámé aplikace"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Tento uživatel nesmí instalovat aplikace"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archivovat"</string>
<string name="update_anyway" msgid="8792432341346261969">"Přesto aktualizovat"</string>
<string name="manage_applications" msgid="5400164782453975580">"Správa aplikací"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nedostatek místa"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Odinstalovat aktualizaci"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"Činnost <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je součástí následující aplikace:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Chcete tuto aplikaci odinstalovat?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Vaše osobní údaje budou uloženy"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archivovat tuto aplikaci pro všechny uživatele? Vaše osobní údaje budou uloženy"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archivovat tuto aplikaci v pracovním profilu? Vaše osobní údaje budou uloženy"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Archivovat tuto aplikaci pro uživatele <xliff:g id="USERNAME">%1$s</xliff:g>? Vaše osobní údaje budou uloženy"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Chcete tuto aplikaci archivovat ze soukromého prostoru? Vaše osobní údaje budou uloženy"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcete tuto aplikaci odinstalovat "<b>"všem"</b>" uživatelům? Aplikace a její údaje budou odstraněny "<b>"všem"</b>" uživatelům tohoto zařízení."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Chcete tuto aplikaci pro uživatele <xliff:g id="USERNAME">%1$s</xliff:g> odinstalovat?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Chcete tuto aplikaci odinstalovat ze svého pracovního profilu?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na tabletu nebo ztrátu dat, která může být používáním aplikace způsobena."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televize a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na televizi nebo ztrátu dat, která může být používáním aplikace způsobena."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Archivovat aplikaci <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Pokračovat"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Nastavení"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalace/odinstalace aplikací pro Wear"</string>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index d7819c7..fb4a1d2 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -26,9 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Appen er installeret."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vil du installere denne app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vil du opdatere denne app?"</string>
- <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>Vil du opdatere denne app via <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Denne app modtager normalt opdateriner fra <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din tablet fremover. Dette kan påvirke appfunktionaliteten.</p>"</string>
- <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>Vil du opdatere denne app via <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Denne app modtager normalt opdateriner fra <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på dit fjernsyn fremover. Dette kan påvirke appfunktionaliteten.</p>"</string>
- <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>Vil du opdatere denne app via <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Denne app modtager normalt opdateriner fra <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din telefon fremover. Dette kan påvirke appfunktionaliteten.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>Vil du opdatere denne app via <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Denne app modtager normalt opdateringer fra <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din tablet fremover. Dette kan påvirke appfunktionaliteten.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>Vil du opdatere denne app via <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Denne app modtager normalt opdateringer fra <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på dit fjernsyn fremover. Dette kan påvirke appfunktionaliteten.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>Vil du opdatere denne app via <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Denne app modtager normalt opdateringer fra <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din telefon fremover. Dette kan påvirke appfunktionaliteten.</p>"</string>
<string name="install_failed" msgid="5777824004474125469">"Appen blev ikke installeret."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakken blev forhindret i at blive installeret."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen blev ikke installeret, da pakken er i strid med en eksisterende pakke."</string>
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Denne bruger kan ikke installere ukendte apps"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Denne bruger har ikke tilladelse til at installere apps"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arkivér"</string>
<string name="update_anyway" msgid="8792432341346261969">"Opdater alligevel"</string>
<string name="manage_applications" msgid="5400164782453975580">"Administrer apps"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Der er ikke mere plads"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Afinstaller opdatering"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er en del af følgende app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Vil du afinstallere denne app?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Dine private data gemmes"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vil du arkivere denne app for alle brugere? Dine private data gemmes"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vil du arkivere denne app fra din arbejdsprofil? Dine private data gemmes"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Vil du arkivere denne app for <xliff:g id="USERNAME">%1$s</xliff:g>? Dine private data gemmes"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vil du arkivere denne app fra dit private område? Dine private data gemmes"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vil du afinstallere denne app for "<b>"alle"</b>" brugere? Appen og dens data fjernes fra "<b>"alle"</b>" brugere på denne enhed."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Vil du afinstallere denne app for brugeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vil du afinstallere denne app fra din arbejdsprofil?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Din tablet og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på din tablet eller tab af data, der kan skyldes brug af appen."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Dit fjernsyn og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på dit fjernsyn eller tab af data, der kan skyldes brug af appen."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Vil du arkivere <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsæt"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Indstillinger"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Installerer/afinstallerer Wear-apps"</string>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index 666ef45..6750008 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Dieser Nutzer darf keine unbekannten Apps installieren"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Dieser Nutzer darf keine Apps installieren"</string>
<string name="ok" msgid="7871959885003339302">"Ok"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archivieren"</string>
<string name="update_anyway" msgid="8792432341346261969">"Trotzdem aktualisieren"</string>
<string name="manage_applications" msgid="5400164782453975580">"Apps verwalten"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Kein freier Speicher vorhanden"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Update deinstallieren"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> gehört zu folgender App:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Möchtest du diese App deinstallieren?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Deine personenbezogenen Daten werden gespeichert"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Diese App für alle Nutzer archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Diese in deinem Arbeitsprofil befindliche App archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Diese App für <xliff:g id="USERNAME">%1$s</xliff:g> archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Möchtest du diese in deinem privaten Bereich befindliche App archivieren? Deine personenbezogenen Daten werden gespeichert."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Möchtest du diese App für "<b>"alle"</b>" Nutzer entfernen? Die App und alle zugehörigen Daten werden für "<b>"alle"</b>" Nutzer des Geräts entfernt."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Möchtest du diese App für den Nutzer <xliff:g id="USERNAME">%1$s</xliff:g> deinstallieren?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Möchtest du diese App aus deinem Arbeitsprofil deinstallieren?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Unbekannte Apps können gefährlich für dein Tablet und deine personenbezogenen Daten sein. Wenn du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Tablet und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Unbekannte Apps können gefährlich für deinen Fernseher und deine personenbezogenen Daten sein. Wenn du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Fernseher und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-Klon"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> archivieren?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Weiter"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Einstellungen"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear-Apps installieren/deinstallieren"</string>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index 94e50b1..6f0db20 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Δεν είναι δυνατή η εγκατάσταση άγνωστων εφαρμογών από αυτόν τον χρήστη"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Δεν επιτρέπεται η εγκατάσταση εφαρμογών σε αυτόν τον χρήστη"</string>
<string name="ok" msgid="7871959885003339302">"ΟΚ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Αρχειοθέτηση"</string>
<string name="update_anyway" msgid="8792432341346261969">"Να ενημερωθεί ούτως ή άλλως"</string>
<string name="manage_applications" msgid="5400164782453975580">"Διαχ. εφαρμογών"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Δεν υπάρχει χώρος"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Απεγκατάσταση ενημέρωσης"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"Η δραστηριότητα <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> αποτελεί τμήμα της ακόλουθης εφαρμογής:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή;"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Τα προσωπικά δεδομένα σας θα αποθηκευτούν"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Αρχειοθέτηση αυτής της εφαρμογής για όλους τους χρήστες; Τα προσωπικά δεδομένα σας θα αποθηκευτούν"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Αρχειοθέτηση αυτής της εφαρμογής στο προφίλ εργασίας σας; Τα προσωπικά δεδομένα σας θα αποθηκευτούν"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Αρχειοθέτηση αυτής της εφαρμογής για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>; Τα προσωπικά δεδομένα σας θα αποθηκευτούν"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Θέλετε να αρχειοθετήσετε αυτή την εφαρμογή από τον απόρρητο χώρο σας; Τα προσωπικά δεδομένα σας θα αποθηκευτούν"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή για "<b>"όλους"</b>" τους χρήστες; Η εφαρμογή και τα δεδομένα της θα καταργηθούν από "<b>"όλους"</b>" τους χρήστες στη συσκευή."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>;"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής από το προφίλ εργασίας σας;"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Το tablet σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για τυχόν βλάβη που μπορεί να προκληθεί στο tablet ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Η τηλεόρασή σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για τυχόν βλάβη που μπορεί να προκληθεί στην τηλεόρασή ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Διπλότυπο <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Αρχειοθέτηση <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>;"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Συνέχεια"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ρυθμίσεις"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Εγκατάσταση/απεγκατάσταση εφαρμογών Wear"</string>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
index b66976a..7537008 100644
--- a/packages/PackageInstaller/res/values-es-rUS/strings.xml
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -26,9 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Se instaló la app."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"¿Deseas instalar esta app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"¿Deseas actualizar esta app?"</string>
- <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>Actualiza esta app desde <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Esta app normalmente recibe actualizaciones desde <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la tablet. Las funciones de la app puede cambiar.</p>"</string>
- <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>Actualiza esta app desde <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Esta app normalmente recibe actualizaciones desde <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la TV. Las funciones de la app puede cambiar.</p>"</string>
- <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>Actualiza esta app desde <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Esta app normalmente recibe actualizaciones desde <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en el teléfono. Las funciones de la app puede cambiar.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>¿Actualizar esta app desde <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Esta app normalmente recibe actualizaciones desde <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la tablet. Las funciones de la app pueden cambiar.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>¿Actualizar esta app desde <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Esta app normalmente recibe actualizaciones desde <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la TV. Las funciones de la app pueden cambiar.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>¿Actualizar esta app desde <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Esta app normalmente recibe actualizaciones desde <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en el teléfono. Las funciones de la app pueden cambiar.</p>"</string>
<string name="install_failed" msgid="5777824004474125469">"No se instaló la app."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Se bloqueó el paquete para impedir la instalación."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"No se instaló la app debido a un conflicto con un paquete."</string>
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Este usuario no puede instalar apps desconocidas"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuario no puede instalar apps"</string>
<string name="ok" msgid="7871959885003339302">"Aceptar"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archivar"</string>
<string name="update_anyway" msgid="8792432341346261969">"Actualizar de todas formas"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gestionar apps"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sin espacio"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar actualización"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> es parte de la siguiente app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"¿Quieres desinstalar esta app?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Se guardarán tus datos personales"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"¿Quieres archivar esta app para todos los usuarios? Se guardarán tus datos personales"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"¿Quieres archivar esta app en tu perfil de trabajo? Se guardarán tus datos personales"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"¿Quieres archivar esta app para <xliff:g id="USERNAME">%1$s</xliff:g>? Se guardarán tus datos personales"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"¿Quieres archivar esta app de tu espacio privado? Se guardarán tus datos personales"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"¿Quieres desinstalar esta app para "<b>"todos"</b>" los usuarios? Se quitarán la aplicación y sus datos de "<b>"todos"</b>" los usuarios del dispositivo."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"¿Quieres desinstalar esta app para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"¿Deseas desinstalar esta app de tu perfil de trabajo?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"La tablet y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra la tablet y de la pérdida de datos que pueda ocasionar su uso."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"La TV y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra la TV y de la pérdida de datos que pueda ocasionar su uso."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"¿Quieres archivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Configuración"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 2a0d063..9a7e523 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Este usuario no puede instalar aplicaciones desconocidas"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuario no tiene permiso para instalar aplicaciones"</string>
<string name="ok" msgid="7871959885003339302">"Aceptar"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archivar"</string>
<string name="update_anyway" msgid="8792432341346261969">"Actualizar igualmente"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gestionar aplicaciones"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sin espacio"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar actualización"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma parte de esta aplicación:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"¿Quieres desinstalar esta aplicación?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Tus datos personales se guardarán."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"¿Archivar esta aplicación para todos los usuarios? Tus datos personales se guardarán."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"¿Archivar esta aplicación en tu perfil de trabajo? Tus datos personales se guardarán."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"¿Archivar esta aplicación para <xliff:g id="USERNAME">%1$s</xliff:g>? Tus datos personales se guardarán."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"¿Quieres archivar esta aplicación de tu espacio privado? Tus datos personales se guardarán."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"¿Quieres desinstalar esta aplicación para "<b>"todos"</b>" los usuarios? La aplicación y sus datos se borrarán de "<b>"todos"</b>" los usuarios del dispositivo."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"¿Quieres desinstalar esta aplicación para el usuario <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"¿Quieres desinstalar esta aplicación de tu perfil de trabajo?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tu tablet y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu tablet o la pérdida de datos que se pueda derivar de su uso."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tu TV y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu TV o la pérdida de datos que se pueda derivar de su uso."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"¿Archivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ajustes"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
index 5d5914e..b083932 100644
--- a/packages/PackageInstaller/res/values-et/strings.xml
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"See kasutaja ei saa installida tundmatuid rakendusi"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Kasutajal ei ole lubatud rakendusi installida"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arhiiv"</string>
<string name="update_anyway" msgid="8792432341346261969">"Värskenda ikkagi"</string>
<string name="manage_applications" msgid="5400164782453975580">"Rakend. haldam."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Pole ruumi"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Värskenduse desinstallimine"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> on osa järgmisest rakendusest:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Kas soovite selle rakenduse desinstallida?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Teie isiklikud andmed salvestatakse"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Kas arhiivida see rakendus kõigi kasutajate puhul? Teie isiklikud andmed salvestatakse"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Kas arhiivida see rakendus teie tööprofiilil? Teie isiklikud andmed salvestatakse"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Kas arhiivida see rakendus kasutaja <xliff:g id="USERNAME">%1$s</xliff:g> puhul? Teie isiklikud andmed salvestatakse"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Kas soovite selle rakenduse oma privaatsest ruumist arhiivida? Teie isiklikud andmed salvestatakse"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Kas soovite selle rakenduse "<b>"kõikide"</b>" kasutajate kontodelt desinstallida? Rakendus ja selle andmed eemaldatakse "<b>"kõikide"</b>" seadme kasutajate kontodelt."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Kas soovite selle rakenduse kasutaja <xliff:g id="USERNAME">%1$s</xliff:g> kontolt desinstallida?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Kas soovite selle rakenduse oma tööprofiililt desinstallida?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Teie tahvelarvuti ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate tahvelarvuti kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Teie teler ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate teleri kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Rakenduse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kloon"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Kas arhiivida <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Jätka"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Seaded"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Weari rak. installimine/desinstallimine"</string>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index af25e52..9d1d536 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Erabiltzaile honek ezin ditu instalatu aplikazio ezezagunak"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Erabiltzaile honek ez du baimenik aplikazioak instalatzeko"</string>
<string name="ok" msgid="7871959885003339302">"Ados"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Artxibatu"</string>
<string name="update_anyway" msgid="8792432341346261969">"Eguneratu halere"</string>
<string name="manage_applications" msgid="5400164782453975580">"Kudeatu aplikazioak"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Ez dago behar adina toki"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstalatu eguneratzea"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> aplikazio honen zati da:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Aplikazioa desinstalatu nahi duzu?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Zure datu pertsonalak gorde egingo dira"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Aplikazio hau erabiltzaile guztientzat artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Aplikazio hau laneko profiletik artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Aplikazio hau <xliff:g id="USERNAME">%1$s</xliff:g> erabiltzailearentzat artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Aplikazio hau eremu pribatutik artxibatu nahi duzu? Zure datu pertsonalak gorde egingo dira."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Erabiltzaile "<b>"guztiei"</b>" desinstalatu nahi diezu aplikazioa? Aplikazioa eta haren datu guztiak ezabatuko zaizkie gailuko erabiltzaile "<b>"guztiei"</b>"."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzaileari desinstalatu nahi diozu aplikazioa?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Aplikazioa laneko profiletik desinstalatu nahi duzu?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Baliteke tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jasatea. Aplikazio hau instalatzen baduzu, onartu egingo duzu hura erabiltzeagatik tabletari agian gertatuko zaizkion kalteen edo datu-galeren erantzulea zeu izango zarela."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Baliteke telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jasatea. Aplikazio hau instalatzen baduzu, onartu egingo duzu hura erabiltzeagatik telebistari agian gertatuko zaizkion kalteen edo datu-galeren erantzulea zeu izango zarela."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> aplikazioaren klona"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> artxibatu nahi duzu?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
index a7dc36a..53bfd27 100644
--- a/packages/PackageInstaller/res/values-fa/strings.xml
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"این کاربر نمیتواند برنامههای ناشناس نصب کند"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"این کاربر مجاز به نصب برنامه نیست"</string>
<string name="ok" msgid="7871959885003339302">"بسیار خوب"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"بایگانی"</string>
<string name="update_anyway" msgid="8792432341346261969">"درهرصورت بهروز شود"</string>
<string name="manage_applications" msgid="5400164782453975580">"مدیریت برنامهها"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"فضا کافی نیست"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"حذف نصب بهروزرسانی"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> قسمتی از برنامه زیر است:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"میخواهید این برنامه را حذف نصب کنید؟"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"دادههای شخصیتان ذخیره خواهد شد"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"این برنامه برای همه کاربران بایگانی شود؟ دادههای شخصیتان ذخیره خواهد شد"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"این برنامه در نمایه کاری بایگانی شود؟ دادههای شخصیتان ذخیره خواهد شد"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"این برنامه برای <xliff:g id="USERNAME">%1$s</xliff:g> بایگانی شود؟ دادههای شخصیتان ذخیره خواهد شد"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"میخواهید این برنامه از فضای خصوصیتان را بایگانی کنید؟ دادههای شخصیتان ذخیره خواهد شد"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"آیا میخواهید این برنامه را برای "<b>"همه"</b>" کاربران حذف کنید؟ این برنامه و دادههای آن برای "<b>"همه"</b>" کاربران این دستگاه حذف خواهد شد."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"آیا میخواهید این برنامه را برای این کاربر <xliff:g id="USERNAME">%1$s</xliff:g> حذف نصب کنید؟"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"میخواهید این برنامه را از نمایه کاریتان حذف نصب کنید؟"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"رایانه لوحی و دادههای شخصیتان دربرابر حمله برنامههای ناشناس آسیبپذیرتر هستند. با نصب این برنامه، موافقت میکنید که مسئول هرگونه آسیب به رایانه لوحی یا از دست رفتن دادهای هستید که ممکن است درنتیجه استفاده از آن به وجود آید."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"تلویزیون و دادههای شخصیتان دربرابر حمله برنامههای ناشناس آسیبپذیرتر هستند. با نصب این برنامه، موافقت میکنید که مسئول هرگونه آسیب به تلویزیون یا از دست رفتن دادهای هستید که ممکن است درنتیجه استفاده از آن به وجود آید."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"همسانه <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> بایگانی شود؟"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ادامه"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"تنظیمات"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"نصب/حذف نصب برنامههای پوشیدنی"</string>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
index 3ef9807..c5bb501 100644
--- a/packages/PackageInstaller/res/values-fi/strings.xml
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Tämä käyttäjä ei voi asentaa tuntemattomia sovelluksia."</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Tämä käyttäjä ei voi asentaa sovelluksia."</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arkistoi"</string>
<string name="update_anyway" msgid="8792432341346261969">"Päivitä silti"</string>
<string name="manage_applications" msgid="5400164782453975580">"Sovellusvalinnat"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Tallennustila ei riitä"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Poista päivitys"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> on osa seuraavaa sovellusta:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Haluatko poistaa tämän sovelluksen?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Henkilökohtainen datasi tallennetaan"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arkistoidaanko tämä sovellus kaikilta käyttäjiltä? Henkilökohtainen datasi tallennetaan"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arkistoidaanko tämä sovellus työprofiilistasi? Henkilökohtainen datasi tallennetaan"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Arkistoidaanko tämä sovellus käyttäjältä: <xliff:g id="USERNAME">%1$s</xliff:g>? Henkilökohtainen datasi tallennetaan"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Haluatko arkistoida tämän sovelluksen yksityisestä tilasta? Henkilökohtainen datasi tallennetaan"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Haluatko poistaa tämän sovelluksen "<b>"kaikilta"</b>" käyttäjiltä? Sovellus ja sen data poistetaan "<b>"kaikilta"</b>" laitteen käyttäjiltä."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Haluatko poistaa tämän sovelluksen käyttäjältä <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Haluatko poistaa sovelluksen työprofiilistasi?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tuntemattomat sovellukset voivat helpommin kaapata tablettisi ja henkilökohtaiset tietosi. Lataamalla sovelluksia tästä lähteestä hyväksyt, että olet itse vastuussa tabletillesi aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tuntemattomat sovellukset voivat helpommin kaapata televisiosi ja henkilökohtaiset tietosi. Lataamalla sovelluksen hyväksyt, että olet itse vastuussa mahdollisista televisiolle aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klooni: <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Arkistoidaanko <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Jatka"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Asetukset"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear-sovellusten asennus/poistaminen"</string>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index 0bd5026..86ba34c 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Cet utilisateur ne peut pas installer d\'applications inconnues"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Cet utilisateur n\'est pas autorisé à installer des applications"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archiver"</string>
<string name="update_anyway" msgid="8792432341346261969">"Mettre à jour malgré tout"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gérer les applis"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Espace insuffisant"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Désinstaller mise à jour"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fait partie de l\'application suivante :"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Voulez-vous désinstaller cette application?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Vos données personnelles seront enregistrées"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archiver cette application pour tous les utilisateurs? Vos données personnelles seront enregistrées"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archiver cette application sur votre profil professionnel? Vos données personnelles seront enregistrées"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Archiver cette application pour <xliff:g id="USERNAME">%1$s</xliff:g>? Vos données personnelles seront enregistrées"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Voulez-vous archiver cette application de votre Espace privé? Vos données personnelles seront enregistrées"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Voulez-vous désinstaller cette application pour "<b>"tous"</b>" les utilisateurs? L\'application et ses données seront supprimées pour "<b>"tous"</b>" les utilisateurs de l\'appareil."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Voulez-vous désinstaller cette application de votre profil professionnel?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données personnelles sont plus vulnérables aux attaques provenant d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de l\'utilisation de telles applications."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données personnelles sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Archiver <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Paramètres"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Installer/désinstaller applis Google Wear"</string>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 1c64613..0b74e19 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -26,9 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Application installée."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Voulez-vous installer cette appli ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Voulez-vous mettre à jour cette appli ?"</string>
- <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>Mettez à jour cette appli depuis <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Elle reçoit normalement des mises à jour depuis <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre tablette. Le fonctionnement de l\'application peut changer.</p>"</string>
- <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>Mettez à jour cette appli depuis <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Elle reçoit normalement des mises à jour depuis <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléviseur. Le fonctionnement de l\'application peut changer.</p>"</string>
- <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>Mettez à jour cette appli depuis <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>?</p><p>Elle reçoit normalement des mises à jour depuis <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application peut changer.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p>Mettre à jour cette appli depuis <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b> ?</p><p>Elle reçoit normalement des mises à jour depuis <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre tablette. Le fonctionnement de l\'application peut changer.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p>Mettre à jour cette appli depuis <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b> ?</p><p>Elle reçoit normalement des mises à jour depuis <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléviseur. Le fonctionnement de l\'application peut changer.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p>Mettre à jour cette appli depuis <b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b> ?</p><p>Elle reçoit normalement des mises à jour depuis <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application peut changer.</p>"</string>
<string name="install_failed" msgid="5777824004474125469">"Application non installée."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"L\'installation du package a été bloquée."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"L\'application n\'a pas été installée, car le package est en conflit avec un package déjà présent."</string>
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Cet utilisateur ne peut pas installer d\'applications inconnues"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Cet utilisateur n\'est pas autorisé à installer des applications"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archiver"</string>
<string name="update_anyway" msgid="8792432341346261969">"Mettre à jour quand même"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gérer applis"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Mémoire insuffisante"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Désinstaller la mise à jour"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fait partie de l\'application suivante :"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Voulez-vous désinstaller cette application ?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Vos données personnelles seront enregistrées."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archiver cette appli pour tous les utilisateurs ? Vos données personnelles seront enregistrées."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archiver cette appli sur votre profil professionnel ? Vos données personnelles seront enregistrées."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Archiver cette appli pour <xliff:g id="USERNAME">%1$s</xliff:g> ? Vos données personnelles seront enregistrées."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Souhaitez-vous archiver cette application de votre espace privé ? Vos données personnelles seront enregistrées."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Voulez-vous désinstaller cette application pour "<b>"tous"</b>" les utilisateurs ? L\'application et ses données seront supprimées pour "<b>"tous"</b>" les utilisateurs de l\'appareil."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Voulez-vous désinstaller cette application pour l\'utilisateur <xliff:g id="USERNAME">%1$s</xliff:g> ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Voulez-vous désinstaller cette appli de votre profil professionnel ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Votre tablette et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de son utilisation."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Votre téléviseur et vos données à caractère personnel sont plus vulnérables aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Archiver <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuer"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Paramètres"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Installer/Désinstaller les applis Wear"</string>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index 38e79ba..96f0fdb 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Este usuario non pode instalar aplicacións descoñecidas"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuario non ten permiso para instalar aplicacións"</string>
<string name="ok" msgid="7871959885003339302">"Aceptar"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arquivar"</string>
<string name="update_anyway" msgid="8792432341346261969">"Actualizar de todas formas"</string>
<string name="manage_applications" msgid="5400164782453975580">"Xestionar apps"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Esgotouse o espazo"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar actualización"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma parte da seguinte aplicación:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Queres desinstalar esta aplicación?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Gardaranse os teus datos persoais"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Queres arquivar esta aplicación para todos os usuarios? Gardaranse os teus datos persoais"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Queres arquivar esta aplicación no teu perfil de traballo? Gardaranse os teus datos persoais"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Queres arquivar esta aplicación para <xliff:g id="USERNAME">%1$s</xliff:g>? Gardaranse os teus datos persoais"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Queres arquivar esta aplicación do teu espazo privado? Gardaranse os teus datos persoais"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Queres desinstalar esta aplicación para "<b>"todos"</b>" os usuarios? A aplicación e os seus datos quitaranse de "<b>"todos"</b>" os usuarios do dispositivo."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Queres desinstalar esta aplicación para o usuario que se chama <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Queres desinstalar esta aplicación do perfil de traballo?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"A tableta e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na tableta ou da perda dos datos que se poidan derivar do seu uso."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"A televisión e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na televisión ou da perda dos datos que se poidan derivar do seu uso."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Queres arquivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Configuración"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps para Wear"</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index 3362a16..18db939 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"આ વપરાશકર્તા અજાણી ઍપ્લિકેશનોને ઇન્સ્ટૉલ કરી શકતા નથી"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"આ વપરાશકર્તાને ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી"</string>
<string name="ok" msgid="7871959885003339302">"ઓકે"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"આર્કાઇવ કરો"</string>
<string name="update_anyway" msgid="8792432341346261969">"તો પણ અપડેટ કરો"</string>
<string name="manage_applications" msgid="5400164782453975580">"ઍપને મેનેજ કરો"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"સ્પેસ નથી"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"અપડેટ અનઇન્સ્ટૉલ કરો"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, નીચેની ઍપ્લિકેશનનો ભાગ છે:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માંગો છો?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"શું આ ઍપને તમામ વપરાશકર્તાઓ માટે આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"શું આ ઍપને તમારી ઑફિસની પ્રોફાઇલ પર આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"શું આ ઍપને <xliff:g id="USERNAME">%1$s</xliff:g> માટે આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"શું તમે તમારી ખાનગી સ્પેસમાંથી આ ઍપને આર્કાઇવ કરવા માગો છો? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"શું તમે "<b>"બધા"</b>" વપરાશકર્તાઓ માટે આ ઍપ્લિકેશનને અનઇન્સ્ટૉલ કરવા માગો છો? ડિવાઇસ પરના "<b>"બધા"</b>" વપરાશકર્તાઓની ઍપ્લિકેશન અને તેનો ડેટા કાઢી નાખવામાં આવશે."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"શું તમે <xliff:g id="USERNAME">%1$s</xliff:g> વપરાશકર્તા માટે આ ઍપ્લિકેશનને અનઇન્સ્ટૉલ કરવા માગો છો?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"શું તમે તમારી ઑફિસની પ્રોફાઇલમાંથી આ ઍપને અનઇન્સ્ટૉલ કરવા માગો છો?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"તમારું ટૅબ્લેટ અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ટૅબ્લેટને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"તમારું ટીવી અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ટીવીને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ની ક્લોન"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> આર્કાઇવ કરીએ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"આગળ વધો"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"સેટિંગ"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"એમ્બેડ ઍપ્લિકેશનો ઇન્સ્ટૉલ/અનઇન્સ્ટૉલ"</string>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index e774291..73cbc33 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"यह उपयोगकर्ता अनजान ऐप्लिकेशन इंस्टॉल नहीं कर सकता"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"इस उपयोगकर्ता को ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है"</string>
<string name="ok" msgid="7871959885003339302">"ठीक है"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"संग्रहित करें"</string>
<string name="update_anyway" msgid="8792432341346261969">"फिर भी अपडेट करें"</string>
<string name="manage_applications" msgid="5400164782453975580">"ऐप्लिकेशन प्रबंधित करें"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"जगह नहीं है"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"अपडेट अनइंस्टॉल करें"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> इस ऐप्लिकेशन का भाग है:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"क्या आपको इस ऐप्लिकेशन को अनइंस्टॉल करना है?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"आपका निजी डेटा सेव किया जाएगा"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"क्या इस ऐप्लिकेशन को सभी के लिए संग्रहित करना है? आपका निजी डेटा सेव किया जाएगा"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"क्या आपको अपनी वर्क प्रोफ़ाइल से यह ऐप्लिकेशन संग्रहित करना है? आपका निजी डेटा सेव किया जाएगा"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"क्या <xliff:g id="USERNAME">%1$s</xliff:g> के लिए यह ऐप्लिकेशन संग्रहित करना है? आपका निजी डेटा सेव किया जाएगा"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"क्या आपको अपने प्राइवेट स्पेस से यह ऐप्लिकेशन संग्रहित करना है? आपका निजी डेटा सेव किया जाएगा"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"क्या आप इस ऐप्लिकेशन को "<b>"सभी"</b>" उपयोगकर्ताओं के लिए अनइंस्टॉल करना चाहते हैं? ऐप्लिकेशन और उसके डेटा को डिवाइस पर "<b>"सभी"</b>" उपयोगकर्ताओं से हटा दिया जाएगा."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"क्या आप उपयोगकर्ता <xliff:g id="USERNAME">%1$s</xliff:g> के लिए इस ऐप्लिकेशन को अनइंस्टॉल करना चाहते हैं?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"क्या अपनी वर्क प्रोफ़ाइल से इस ऐप्लिकेशन को अनइंस्टॉल करना है?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"आपका टैबलेट और निजी डेटा अनजान ऐप्लिकेशन के हमले को लेकर ज़्यादा संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके, आप सहमति देते हैं कि इसके इस्तेमाल के चलते आपके टैबलेट को होने वाले किसी भी नुकसान या डेटा के मिट जाने पर, आप ज़िम्मेदार होंगे."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"आपका टीवी और निजी डेटा अनजान ऐप्लिकेशन के हमले को लेकर ज़्यादा संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके, आप सहमति देते हैं कि इसके इस्तेमाल के चलते आपके टीवी को होने वाले किसी भी नुकसान या डेटा के मिट जाने पर, आप ज़िम्मेदार होंगे."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> का क्लोन"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"क्या <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> संग्रहित करना है?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"जारी रखें"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"सेटिंग"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear ऐप्लिकेशन इंस्टॉल/अनइंस्टॉल हो रहे हैं"</string>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
index 5df276e..27ba153 100644
--- a/packages/PackageInstaller/res/values-hr/strings.xml
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ovaj korisnik ne može instalirati nepoznate aplikacije"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ovaj korisnik nema dopuštenje za instaliranje aplikacija"</string>
<string name="ok" msgid="7871959885003339302">"U redu"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arhiviraj"</string>
<string name="update_anyway" msgid="8792432341346261969">"Ipak ažuriraj"</string>
<string name="manage_applications" msgid="5400164782453975580">"Upravljanje apl."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nema dovoljno mjesta"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Deinstalacija ažuriranja"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"Aktivnost <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> dio je sljedeće aplikacije:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Želite li deinstalirati ovu aplikaciju?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Vaši će se osobni podaci spremiti"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Želite li arhivirati ovu aplikaciju za sve korisnike? Vaši će se osobni podaci spremiti"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Želite li arhivirati ovu aplikaciju na poslovnom profilu? Vaši će se osobni podaci spremiti"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Želite li arhivirati ovu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>? Vaši će se osobni podaci spremiti"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Želite li arhivirati ovu aplikaciju iz svojeg privatnog prostora? Vaši će se osobni podaci spremiti"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Želite li deinstalirati tu aplikaciju za "<b>"sve"</b>" korisnike? Aplikacija i njezini podaci bit će uklonjeni sa "<b>"svih"</b>" korisnika na uređaju."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Želite li deinstalirati tu aplikaciju za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Želite li deinstalirati tu aplikaciju s poslovnog profila?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Vaš tablet i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje tableta ili gubitak podataka do kojih može doći uslijed njezine upotrebe."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Vaš TV i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje televizora ili gubitak podataka do kojih može doći uslijed njezine upotrebe."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Želite li arhivirati aplikaciju <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Nastavi"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Postavke"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instaliranje/deinstaliranje Wear apl."</string>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
index 6de5bac..b2c7c1a 100644
--- a/packages/PackageInstaller/res/values-hu/strings.xml
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ez a felhasználó nem telepíthet ismeretlen alkalmazásokat"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ez a felhasználó nem telepíthet alkalmazásokat"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archiválás"</string>
<string name="update_anyway" msgid="8792432341346261969">"Frissítés"</string>
<string name="manage_applications" msgid="5400164782453975580">"Alkalmazáskezelés"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nincs elég hely"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Frissítés eltávolítása"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"A(z) <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> a következő alkalmazás része:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Eltávolítja ezt az alkalmazást?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Személyes adatait menteni fogja a rendszer."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Valamennyi felhasználó számára archiválja az alkalmazást? Személyes adatait menteni fogja a rendszer."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archiválja ezt az alkalmazást a munkaprofilján? Személyes adatait menteni fogja a rendszer."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Archiválja ezt az alkalmazást <xliff:g id="USERNAME">%1$s</xliff:g> számára? Személyes adatait menteni fogja a rendszer."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Archiválja ezt az alkalmazást a privát területről? Személyes adatait menteni fogja a rendszer."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Szeretné eltávolítani ezt az alkalmazást "<b>"minden"</b>" felhasználónál? Az alkalmazást és adatait az eszköz "<b>"minden"</b>" felhasználójánál töröljük."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Eltávolítja ezt az alkalmazást <xliff:g id="USERNAME">%1$s</xliff:g> felhasználó esetében?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Biztosan eltávolítja ezt az alkalmazást a munkaprofiljából?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Táblagépe és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elfogadja, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a táblagépet ért károkért."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Tévéje és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elfogadja, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a tévét ért károkért."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klónozott <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Archiválja ezt: <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Tovább"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Beállítások"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear-alkalmazások telepítése/törlése"</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index 4bb3915..bbc1974 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Այս օգտատերը չի կարող անհայտ հավելվածներ տեղադրել"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Այս օգտատիրոջը չի թույլատրվում տեղադրել հավելվածներ"</string>
<string name="ok" msgid="7871959885003339302">"Եղավ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Արխիվացնել"</string>
<string name="update_anyway" msgid="8792432341346261969">"Թարմացնել"</string>
<string name="manage_applications" msgid="5400164782453975580">"Կառավարել հավելվածները"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Բավարար տարածք չկա"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Ապատեղադրել թարմացումը"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> գործողությունը հետևյալ հավելվածի մասն է`"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Ուզո՞ւմ եք ապատեղադրել այս հավելվածը։"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Ձեր անձնական տվյալները կպահվեն"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Արխիվացնե՞լ այս հավելվածը բոլոր օգտատերերի համար։ Ձեր անձնական տվյալները կպահվեն"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Արխիվացնե՞լ այս հավելվածը ձեր աշխատանքային պրոֆիլում։ Ձեր անձնական տվյալները կպահվեն"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Արխիվացնե՞լ այս հավելվածը <xliff:g id="USERNAME">%1$s</xliff:g> օգտատիրոջ համար։ Ձեր անձնական տվյալները կպահվեն"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Արխիվացնե՞լ այս հավելվածը ձեր անձնական տարածքից։ Ձեր անձնական տվյալները կպահվեն"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ապատեղադրե՞լ այս հավելվածը "<b>"բոլոր"</b>" օգտատերերի համար: Հավելվածը և դրա տվյալները կհեռացվեն սարքի "<b>"բոլոր"</b>" օգտատերերից:"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Ապատեղադրե՞լ այս հավելվածը <xliff:g id="USERNAME">%1$s</xliff:g> օգտատիրոջ համար:"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Հեռացնե՞լ այս հավելվածը ձեր աշխատանքային պրոֆիլից"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ձեր պլանշետը և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր պլանշետին հասցված ցանկացած վնասի կամ տվյալների կորստի համար:"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Ձեր հեռուստացույցը և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր հեռուստացույցին հասցված ցանկացած վնասի կամ տվյալների կորստի համար:"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"«<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>» հավելվածի կլոն"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Արխիվացնե՞լ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> հավելվածը"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Շարունակել"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Կարգավորումներ"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear հավելվածների տեղադրում/ապատեղադրում"</string>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
index 58db084..d4f3e76 100644
--- a/packages/PackageInstaller/res/values-in/strings.xml
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplikasi yang tidak dikenal tidak dapat diinstal oleh pengguna ini"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Pengguna ini tidak diizinkan menginstal aplikasi"</string>
<string name="ok" msgid="7871959885003339302">"Oke"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Mengarsipkan"</string>
<string name="update_anyway" msgid="8792432341346261969">"Tetap update"</string>
<string name="manage_applications" msgid="5400164782453975580">"Kelola aplikasi"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Kehabisan ruang penyimpanan"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Uninstal update"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> adalah bagian dari aplikasi berikut:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Apakah Anda ingin meng-uninstal aplikasi ini?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Data pribadi Anda akan disimpan"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arsipkan aplikasi ini untuk semua pengguna? Data pribadi Anda akan disimpan"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arsipkan aplikasi ini di profil kerja? Data pribadi Anda akan disimpan"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Arsipkan aplikasi untuk <xliff:g id="USERNAME">%1$s</xliff:g>? Data pribadi Anda akan disimpan"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ingin mengarsipkan aplikasi ini dari ruang pribadi? Data pribadi Anda akan disimpan"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Apakah Anda ingin meng-uninstal aplikasi ini untuk "<b>"semua"</b>" pengguna? Aplikasi dan datanya akan dihapus dari "<b>"semua"</b>" pengguna pada perangkat."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Apakah Anda ingin meng-uninstal aplikasi ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ingin meng-uninstal aplikasi ini dari profil kerja Anda?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet dan data pribadi Anda lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan tablet atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV dan data pribadi Anda lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan TV atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Arsipkan <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Lanjutkan"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Setelan"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Melakukan instal/uninstal aplikasi Wear"</string>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
index 44fc208..32b686e 100644
--- a/packages/PackageInstaller/res/values-is/strings.xml
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Þessi notandi getur ekki sett upp óþekkt forrit"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Þessi notandi hefur ekki leyfi til að setja upp forrit"</string>
<string name="ok" msgid="7871959885003339302">"Í lagi"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Setja í geymslu"</string>
<string name="update_anyway" msgid="8792432341346261969">"Uppfæra samt"</string>
<string name="manage_applications" msgid="5400164782453975580">"Stj. forritum"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Ekkert pláss eftir"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Fjarlægja uppfærslu"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er hluti af þessu forriti:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Viltu fjarlægja þetta forrit?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Persónuupplýsingarnar þínar verða vistaðar"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Setja þetta forrit í geymslu fyrir alla notendur? Persónuupplýsingarnar þínar verða vistaðar"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Setja þetta forrit á geymslu á vinnusniðinu þínu? Persónuupplýsingarnar þínar verða vistaðar"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Setja þetta forrit í geymslu fyrir <xliff:g id="USERNAME">%1$s</xliff:g>? Persónuupplýsingarnar þínar verða vistaðar"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Viltu setja þetta forrit úr einkarýminu í geymslu? Persónuupplýsingarnar þínar verða vistaðar"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Viltu fjarlægja þetta forrit hjá "<b>"öllum"</b>" notendum? Forritið og gögn þess verða fjarlægð hjá "<b>"öllum"</b>" notendum tækisins."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Viltu fjarlægja þetta forrit fyrir notandann <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Viltu fjarlægja þetta forrit af vinnuprófílnum þínum?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Spjaldtölvan þín og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á spjaldtölvunni eða gagnatapi sem leiða kann af notkun þess."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Sjónvarpið þitt og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á sjónvarpinu eða gagnatapi sem leiða kann af notkun þess."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Afrit af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Setja <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> í geymslu?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Áfram"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Stillingar"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Uppsetning/fjarlæging Wear forrita"</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 136a537..3fb3846 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Questo utente non può installare app sconosciute"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"L\'utente non è autorizzato a installare app"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archivia"</string>
<string name="update_anyway" msgid="8792432341346261969">"Aggiorna comunque"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gestisci app"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Spazio esaurito"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Disinstalla aggiornamento"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> fa parte della seguente applicazione:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Vuoi disinstallare questa app?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"I tuoi dati personali verranno salvati"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vuoi archiviare questa app per tutti gli utenti? I tuoi dati personali verranno salvati"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vuoi archiviare questa app che si trova nel tuo profilo di lavoro? I tuoi dati personali verranno salvati"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Vuoi archiviare questa app per <xliff:g id="USERNAME">%1$s</xliff:g>? I tuoi dati personali verranno salvati"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vuoi archiviare questa app che si trova nel tuo spazio privato? I tuoi dati personali verranno salvati"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vuoi disinstallare questa applicazione per "<b>"tutti"</b>" gli utenti? L\'applicazione e i relativi dati verranno rimossi da "<b>"tutti"</b>" gli utenti configurati sul dispositivo."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Disinstallare l\'app per l\'utente <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vuoi disinstallare questa app dal tuo profilo di lavoro?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"I dati del tablet e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni al tablet o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"I dati della TV e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni alla TV o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone di <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Vuoi archiviare <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continua"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Impostazioni"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Installazione/disinstallazione app Wear"</string>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 2c6f127..dcec0e0 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"למשתמש הזה אין הרשאה להתקין אפליקציות שאינן מוכרות"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"למשתמש הזה אין הרשאה להתקין אפליקציות"</string>
<string name="ok" msgid="7871959885003339302">"אישור"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"העברה לארכיון"</string>
<string name="update_anyway" msgid="8792432341346261969">"אני רוצה לעדכן בכל זאת"</string>
<string name="manage_applications" msgid="5400164782453975580">"ניהול אפליקציות"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"אין מספיק מקום"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"הסרת התקנה של עדכון"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"הפעילות <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> היא חלק מהאפליקציה הבאה:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"להסיר את ההתקנה של האפליקציה הזו?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"המידע האישי שלך יישמר"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"להעביר לארכיון את האפליקציה הזו לכל המשתמשים? המידע האישי שלך יישמר"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"להעביר את האפליקציה הזו מפרופיל העבודה לארכיון? המידע האישי שלך יישמר"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"להעביר את האפליקציה הזו של <xliff:g id="USERNAME">%1$s</xliff:g> לארכיון? המידע האישי שלך יישמר"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"להעביר את האפליקציה הזו מהמרחב הפרטי לארכיון? המידע האישי שלך יישמר"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"להסיר את האפליקציה הזו עבור "<b>"כל"</b>" המשתמשים? האפליקציה והנתונים שלה יוסרו עבור "<b>"כל"</b>" המשתמשים במכשיר."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"להסיר את ההתקנה של האפליקציה הזו עבור <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"רוצה להסיר את האפליקציה הזו מפרופיל העבודה?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"נתוני הטאבלט והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. התקנת האפליקציה הזו מהווה את הסכמתך לכך שהאחריות הבלעדית היא שלך במקרה של אובדן נתונים או גרימת נזק לטאבלט בעקבות השימוש באפליקציה."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"נתוני הטלוויזיה והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. התקנת האפליקציה הזו מהווה את הסכמתך לכך שהאחריות הבלעדית היא שלך במקרה של אובדן נתונים או גרימת נזק לטלוויזיה שלך בעקבות השימוש באפליקציה."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"שכפול של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"להעביר את <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> לארכיון?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"המשך"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"הגדרות"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"תהליך התקנה/הסרת התקנה של אפליקציות Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
index dd0d00e..4d16b1a 100644
--- a/packages/PackageInstaller/res/values-ja/strings.xml
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"このユーザーは不明なアプリをインストールできません"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"このユーザーはアプリをインストールできません"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"アーカイブ"</string>
<string name="update_anyway" msgid="8792432341346261969">"更新する"</string>
<string name="manage_applications" msgid="5400164782453975580">"アプリの管理"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"容量不足"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"アップデートのアンインストール"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> は次のアプリの一部です。"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"このアプリをアンインストールしますか?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"個人データが保存されます"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"すべてのユーザーに対しこのアプリをアーカイブしますか?個人データが保存されます"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"仕事用プロファイルでこのアプリをアーカイブしますか?個人データが保存されます"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> に対しこのアプリをアーカイブしますか?個人データが保存されます"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"プライベート スペースからこのアプリをアーカイブしますか?個人データが保存されます"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"このアプリを"<b>"すべての"</b>"ユーザーからアンインストールしますか?このアプリとそのデータはデバイスの"<b>"すべての"</b>"ユーザーから削除されます。"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> さんのアプリをアンインストールしますか?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"このアプリを仕事用プロファイルからアンインストールしますか?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"不明なアプリをインストールするとタブレットや個人データの侵害に対する安全性が低下します。このアプリをインストールすることで、アプリの使用により生じる可能性があるタブレットへの侵害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意することになります。"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"不明なアプリをインストールするとテレビや個人データの侵害に対する安全性が低下します。このアプリをインストールすることで、アプリの使用により生じる可能性があるテレビへの侵害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意することになります。"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> のクローン"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> をアーカイブしますか?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"次へ"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"設定"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wearアプリ インストール/アンインストール"</string>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
index 7d4c1fd..1a459af 100644
--- a/packages/PackageInstaller/res/values-ka/strings.xml
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ამ მომხმარებელს არ შეუძლია უცნობი აპების ინსტალაცია"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ამ მომხმარებელს არ აქვს აპების ინსტალაციის უფლება"</string>
<string name="ok" msgid="7871959885003339302">"კარგი"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"არქივი"</string>
<string name="update_anyway" msgid="8792432341346261969">"მაინც განახლდეს"</string>
<string name="manage_applications" msgid="5400164782453975580">"აპების მართვა"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"მეხსიერება არასაკმარისია"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"განახლების დეინსტალაცია"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> არის შემდეგი აპის ნაწილი:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"გსურთ ამ აპის დეინსტალაცია?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"თქვენი პერსონალური მონაცემები შეინახება"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"გსურთ ამ აპის ყველა მომხმარებლისთვის დაარქივება? თქვენი პერსონალური მონაცემები შეინახება"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"გსურთ ამ აპის დაარქივება თქვენს სამსახურის პროფილში? თქვენი პერსონალური მონაცემები შეინახება"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"გსურთ ამ აპის დაარქივება <xliff:g id="USERNAME">%1$s</xliff:g>-ისთვის? თქვენი პერსონალური მონაცემები შეინახება"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"გსურთ ამ აპის დაარქივება თქვენი კერძო სივრციდან? თქვენი პერსონალური მონაცემები შეინახება"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"გსურთ ამ აპის დეინსტალაცია "<b>"ყველა"</b>" მომხმარებლისთვის? აპლიკაცია და მისი მონაცემები ამოიშლება "<b>"ყველა"</b>" მომხმარებლის პროფილიდან მოწყობილობაზე."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"გსურთ ამ აპის დეინსტალაცია <xliff:g id="USERNAME">%1$s</xliff:g>-ისთვის?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"გსურთ ამ აპის დეინსტალაცია თქვენი სამსახურის პროფილიდან?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"თქვენი ტაბლეტი და პერსონალური მონაცემები მეტად დაუცველია უცნობი აპების მხრიდან შეტევების წინაშე. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტაბლეტისთვის მიყენებულ ზიანსა თუ მონაცემების დაკარგვაზე."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"თქვენი ტელევიზორი და პერსონალური მონაცემები მეტად დაუცველია უცნობი აპების მხრიდან შეტევების წინაშე. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტელევიზორისთვის მიყენებულ ზიანსა თუ მონაცემების დაკარგვაზე."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> კლონის შექმნა"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"გსურთ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-ის დაარქივება?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"გაგრძელება"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"პარამეტრები"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear აპების ინსტალაცია/დეინსტალაცია"</string>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
index f731bbc..718ca1d 100644
--- a/packages/PackageInstaller/res/values-kk/strings.xml
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Бұл пайдаланушы белгісіз қолданбаларды орната алмайды"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Бұл пайдаланушының қолданбаларды орнату рұқсаты жоқ"</string>
<string name="ok" msgid="7871959885003339302">"Жарайды"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Мұрағаттау"</string>
<string name="update_anyway" msgid="8792432341346261969">"Бәрібір жаңарту"</string>
<string name="manage_applications" msgid="5400164782453975580">"Қолданбаларды басқару"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Орын жоқ"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Жаңа нұсқаны жою"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> келесі қолданбаның бөлігі:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Осы қолданба жойылсын ба?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Жеке деректеріңіз сақталады."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Бұл қолданбаны барлық пайдаланушы үшін мұрағаттау керек пе? Жеке деректеріңіз сақталады."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Бұл қолданбаны жұмыс профиліңізде мұрағаттау керек пе? Жеке деректеріңіз сақталады."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Бұл қолданбаны <xliff:g id="USERNAME">%1$s</xliff:g> үшін мұрағаттау керек пе? Жеке деректеріңіз сақталады."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Осы құрылғыны жеке бөлмеңізде мұрағаттағыңыз келе ме? Жеке деректеріңіз сақталады."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Бұл қолданба "<b>"барлық"</b>" пайдаланушылар үшін жойылсын ба? Қолданба және оның деректері құрылғыдағы "<b>"барлық"</b>" пайдаланушылардан өшіріледі."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> үшін осы қолданба жойылсын ба?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Бұл қолданба жұмыс профиліңізден жойылсын ба?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Планшет және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі планшетке келетін залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Теледидар және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі теледидарға келетін қандай да бір залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клоны"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> белгісін мұрағаттау керек пе?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Жалғастыру"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Параметрлер"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear қолданбасын орнату/жою"</string>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
index 7ae0ce7..3034f88 100644
--- a/packages/PackageInstaller/res/values-km/strings.xml
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"អ្នកប្រើប្រាស់នេះមិនអាចដំឡើងកម្មវិធីមិនស្គាល់បានទេ"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"មិនអនុញ្ញាតឱ្យអ្នកប្រើប្រាស់នេះដំឡើងកម្មវិធីទេ"</string>
<string name="ok" msgid="7871959885003339302">"យល់ព្រម"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ទុកក្នុងបណ្ណសារ"</string>
<string name="update_anyway" msgid="8792432341346261969">"មិនអីទេ ដំឡើងកំណែចុះ"</string>
<string name="manage_applications" msgid="5400164782453975580">"គ្រប់គ្រងកម្មវិធី"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"អស់ទំហំផ្ទុក"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"លុបកំណែថ្មី"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ជាផ្នែកមួយនៃកម្មវិធីដូចខាងក្រោមនេះ៖"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"តើអ្នកចង់លុបកម្មវិធីនេះដែរទេ?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"ទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកនឹងត្រូវបានរក្សាទុក"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ទុកកម្មវិធីនេះក្នុងបណ្ណសារសម្រាប់អ្នកប្រើប្រាស់ទាំងអស់ឬ? ទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកនឹងត្រូវបានរក្សាទុក"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ទុកកម្មវិធីនេះក្នុងបណ្ណសារនៅលើកម្រងព័ត៌មានការងាររបស់អ្នកឬ? ទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកនឹងត្រូវបានរក្សាទុក"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"ទុកកម្មវិធីនេះក្នុងបណ្ណសារសម្រាប់ <xliff:g id="USERNAME">%1$s</xliff:g> ឬ? ទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកនឹងត្រូវបានរក្សាទុក"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"តើអ្នកចង់ទុកកម្មវិធីនេះក្នុងបណ្ណសារពីលំហឯកជនរបស់អ្នកដែរទេ? ទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកនឹងត្រូវបានរក្សាទុក"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"តើអ្នកចង់លុបកម្មវិធីនេះសម្រាប់អ្នកប្រើប្រាស់"<b>"ទាំងអស់"</b>"ដែរទេ? កម្មវិធីនេះ និងទិន្នន័យរបស់វានឹងត្រូវបានលុបចេញពីអ្នកប្រើប្រាស់"<b>"ទាំងអស់"</b>"នៅលើឧបករណ៍នេះ។"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"តើអ្នកចង់លុបកម្មវិធីនេះសម្រាប់អ្នកប្រើប្រាស់ <xliff:g id="USERNAME">%1$s</xliff:g> ដែរទេ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"តើអ្នកចង់លុបកម្មវិធីនេះពីកម្រងព័ត៌មានការងាររបស់អ្នកដែរទេ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ថេប្លេត និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតទាំងឡាយចំពោះថេប្លេត ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្ដាលមកពីការប្រើប្រាស់កម្មវិធីនេះ។"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ទូរទស្សន៍ និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតទាំងឡាយចំពោះទូរទស្សន៍ ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្ដាលមកពីការប្រើប្រាស់កម្មវិធីនេះ។"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"ក្លូន <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"ទុក <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ក្នុងបណ្ណសារឬ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"បន្ត"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ការកំណត់"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"ការដំឡើង/ការលុបកម្មវិធីឧបករណ៍ពាក់"</string>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
index ec0746d..e65770f 100644
--- a/packages/PackageInstaller/res/values-kn/strings.xml
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ಈ ಬಳಕೆದಾರರು ಅಪರಿಚಿತ ಆ್ಯಪ್ಗಳನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ಆ್ಯಪ್ಗಳನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಈ ಬಳಕೆದಾರರನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
<string name="ok" msgid="7871959885003339302">"ಸರಿ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ಆರ್ಕೈವ್ ಮಾಡಿ"</string>
<string name="update_anyway" msgid="8792432341346261969">"ಪರವಾಗಿಲ್ಲ, ಅಪ್ಡೇಟ್ ಮಾಡಿ"</string>
<string name="manage_applications" msgid="5400164782453975580">"ಆ್ಯಪ್ ನಿರ್ವಹಿಸಿ"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ಸಂಗ್ರಹಣೆ ಖಾಲಿ ಇಲ್ಲ"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"ಅಪ್ಡೇಟ್ ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ಎಂಬುದು ಕೆಳಗಿನ ಆ್ಯಪ್ನ ಭಾಗವಾಗಿದೆ:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಬಯಸುವಿರಾ?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಸೇವ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ಎಲ್ಲಾ ಬಳಕೆದಾರರಿಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಬೇಕೆ? ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಸೇವ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ನಲ್ಲಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಬೇಕೆ? ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಸೇವ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"ಈ ಆ್ಯಪ್ ಅನ್ನು <xliff:g id="USERNAME">%1$s</xliff:g> ಗಾಗಿ ಆರ್ಕೈವ್ ಮಾಡಬೇಕೆ? ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಸೇವ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ನಿಮ್ಮ ಖಾಸಗಿ ಸ್ಪೇಸ್ನಿಂದ ಈ ಆ್ಯಪ್ ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಲು ನೀವು ಬಯಸುತ್ತೀರಾ? ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಸೇವ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ನೀವು "<b>"ಎಲ್ಲಾ"</b>" ಬಳಕೆದಾರರಿಗೂ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಬಯಸುವಿರಾ? ಸಾಧನದಲ್ಲಿನ "<b>"ಎಲ್ಲಾ"</b>" ಬಳಕೆದಾರರಿಂದ ಆ್ಯಪ್ ಮತ್ತು ಅದರ ಡೇಟಾವನ್ನು ತೆಗೆದುಹಾಕಲಾಗುವುದು."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> ಬಳಕೆದಾರರಿಗೆ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ನೀವು ಬಯಸುವಿರಾ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ನಿಮ್ಮ ಉದ್ಯೋಗದ ಪ್ರೊಫೈಲ್ನಿಂದ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ನೀವು ಬಯಸುವಿರಾ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಆ್ಯಪ್ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡುವ ಮೂಲಕ, ನಿಮ್ಮ ಫೋನ್ಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ನಿಮ್ಮ ಟಿವಿ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಆ್ಯಪ್ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡುವ ಮೂಲಕ, ನಿಮ್ಮ ಟಿವಿಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಕ್ಲೋನ್"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಬೇಕೆ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ಮುಂದುವರಿಸಿ"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"wear ಆ್ಯಪ್ಗಳನ್ನು ಇನ್ಸ್ಟಾಲ್/ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
index 9fbff25..fbe8969 100644
--- a/packages/PackageInstaller/res/values-ko/strings.xml
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -28,7 +28,7 @@
<string name="install_confirm_question_update" msgid="3348888852318388584">"이 앱을 업데이트하시겠습니까?"</string>
<string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="7994800761970572198">"<p><b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서</b>?</p><p>이 앱을 업데이트하세요. 이 앱은 보통<b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>에서 업데이트를 받습니다. 다른 출처에서 업데이트를 받으면 향후 태블릿에 있는 어떤 출처에서든지 업데이트를 받을 수 있습니다. 앱 기능이 변경될 수 있습니다.</p>"</string>
<string name="install_confirm_question_update_owner_reminder" product="tv" msgid="2435174886412089791">"<p><b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서</b>?</p><p>이 앱을 업데이트하세요. 이 앱은 보통<b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>에서 업데이트를 받습니다. 다른 출처에서 앱을 업데이트하면 향후 TV에 있는 어떤 출처에서든지 업데이트를 받을 수 있습니다. 앱 기능이 변경될 수 있습니다.</p>"</string>
- <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p><b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서</b>?</p><p>이 앱을 업데이트하세요. 이 앱은 보통<b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>에서 업데이트를 받습니다. 다른 출처에서 업데이트를 받으면 향후 휴대전화에 있는 어떤 출처든지 업데이트를 받을 수 있습니다. 앱 기능이 변경될 수 있습니다.</p>"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="7155138616126795839">"<p><b><xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g></b>에서 이 앱을 업데이트하시겠어요?</p><p>이 앱은 보통 <b><xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g></b>에서 업데이트를 받습니다. 다른 출처에서 앱을 업데이트하면 향후 휴대전화에 있는 어떤 출처에서나 업데이트를 받을 수 있으며, 앱 기능이 변경될 수 있습니다.</p>"</string>
<string name="install_failed" msgid="5777824004474125469">"앱이 설치되지 않았습니다."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"패키지 설치가 차단되었습니다."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"패키지가 기존 패키지와 충돌하여 앱이 설치되지 않았습니다."</string>
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"이 사용자는 알 수 없는 앱을 설치할 수 없습니다."</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"이 사용자는 앱을 설치할 권한이 없습니다."</string>
<string name="ok" msgid="7871959885003339302">"확인"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"보관처리"</string>
<string name="update_anyway" msgid="8792432341346261969">"업데이트"</string>
<string name="manage_applications" msgid="5400164782453975580">"앱 관리"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"여유 공간이 없음"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"업데이트 제거"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>은(는) 다음 앱의 일부입니다."</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"이 앱을 제거하시겠습니까?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"내 개인 정보가 저장됩니다."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"모든 사용자를 대상으로 이 앱을 보관처리하시겠습니까? 내 개인 정보가 저장됩니다."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"직장 프로필에서 이 앱을 보관처리하시겠습니까? 내 개인 정보가 저장됩니다."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>님을 대상으로 이 앱을 보관처리하시겠습니까? 내 개인 정보가 저장됩니다."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"비공개 스페이스에서 이 앱을 보관처리하시겠습니까? 내 개인 정보가 저장됩니다."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119"><b>"모든"</b>" 사용자에 대해 이 앱을 제거하시겠습니까? 기기를 사용하는 "<b>"모든"</b>" 사용자에 대해 애플리케이션 및 데이터가 삭제됩니다."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g>님의 기기에 설치된 앱을 제거하시겠습니까?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"직장 프로필에서 이 앱을 제거하시겠습니까?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"태블릿과 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 태블릿 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV와 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 TV 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 복제"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 앱을 보관처리하시겠습니까?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"계속"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"설정"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear 앱 설치/제거"</string>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index b6d3e21..8b2d425 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Бул колдонуучу белгисиз колдонмолорду орното албайт"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Бул колдонуучу колдонмолорду орното албайт"</string>
<string name="ok" msgid="7871959885003339302">"ЖАРАЙТ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Архивдөө"</string>
<string name="update_anyway" msgid="8792432341346261969">"Баары бир жаңыртылсын"</string>
<string name="manage_applications" msgid="5400164782453975580">"Колд. башкаруу"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Бош орун жок"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Жаңыртууну чыгарып салуу"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> төмөнкү колдонмонун бөлүгү:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Бул колдонмону чыгарып саласызбы?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Жеке маалыматыңыз сакталат"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Бул колдонмо бардык колдонуучулар үчүн архивделсинби? Жеке маалыматыңыз сакталат"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Бул колдонмо жумуш профилиңизде архивделсинби? Жеке маалыматыңыз сакталат"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Бул колдонмо <xliff:g id="USERNAME">%1$s</xliff:g> үчүн архивделсинби? Жеке маалыматыңыз сакталат"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Бул колдонмо жеке чөйрөдөн архивделсинби? Жеке маалыматыңыз сакталат"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Бул колдонмо "<b>"бардык"</b>" колдонуучулардан алынып салынсынбы? Бул колдонмо жана анын дайындары бул түзмөктүн "<b>"бардык"</b>" колдонуучуларынан өчүрүлөт."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Бул колдонмону <xliff:g id="USERNAME">%1$s</xliff:g> үчүн чыгарып саласызбы?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Бул колдонмону жумуш профилиңизден чыгарып саласызбы?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Планшетиңиз жана жеке дайын-даректериңиз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам планшетиңизге кандайдыр бир зыян келтирилсе же дайын-даректериңизды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Сыналгыңыз жана жеке дайын-даректериңиз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам сыналгыңызга кандайдыр бир зыян келтирилсе же дайын-даректериңизды жоготуп алсаңыз, өзүңүз жооптуу болосуз."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клону"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> архивделсинби?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Улантуу"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Параметрлер"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Тагынма колдонмолорду орнотуу/чыгаруу"</string>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
index d9b33ff..8a95398 100644
--- a/packages/PackageInstaller/res/values-lo/strings.xml
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ຜູ້ໃຊ້ນີ້ບໍ່ສາມາດຕິດຕັ້ງແອັບທີ່ບໍ່ຮູ້ຈັກໄດ້"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ຜູ້ໃຊ້ນີ້ບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ຕິດຕັ້ງແອັບໄດ້"</string>
<string name="ok" msgid="7871959885003339302">"ຕົກລົງ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ເກັບໄວ້ໃນແຟ້ມ"</string>
<string name="update_anyway" msgid="8792432341346261969">"ຢືນຢັນການອັບເດດ"</string>
<string name="manage_applications" msgid="5400164782453975580">"ຈັດການແອັບ"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ພື້ນທີ່ຫວ່າງບໍ່ພຽງພໍ"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"ຖອນການຕິດຕັ້ງອັບເດດ"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ແມ່ນສ່ວນໜຶ່ງຂອງແອັບຕໍ່ໄປນີ້:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບນີ້ບໍ່?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"ລະບົບຈະບັນທຶກຂໍ້ມູນສ່ວນຕົວຂອງທ່ານໄວ້"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ເກັບແອັບນີ້ໄວ້ໃນແຟ້ມສຳລັບຜູ້ໃຊ້ທຸກຄົນບໍ? ລະບົບຈະບັນທຶກຂໍ້ມູນສ່ວນຕົວຂອງທ່ານໄວ້"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ເກັບແອັບນີ້ໄວ້ໃນແຟ້ມຢູ່ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານບໍ? ລະບົບຈະບັນທຶກຂໍ້ມູນສ່ວນຕົວຂອງທ່ານໄວ້"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"ເກັບແອັບນີ້ໄວ້ໃນແຟ້ມສຳລັບ <xliff:g id="USERNAME">%1$s</xliff:g> ບໍ? ລະບົບຈະບັນທຶກຂໍ້ມູນສ່ວນຕົວຂອງທ່ານໄວ້"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ທ່ານຕ້ອງການເກັບແອັບນີ້ໄວ້ໃນແຟ້ມແຍກຈາກພື້ນທີ່ສ່ວນຕົວຂອງທ່ານບໍ? ລະບົບຈະບັນທຶກຂໍ້ມູນສ່ວນຕົວຂອງທ່ານໄວ້"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ທ່ານຕ້ອງການທີ່ຈະຖອນການຕິດຕັ້ງແອັບນີ້ສຳລັງຜູ້ໃຊ້"<b>"ທຸກຄົນ"</b>"ບໍ່? ແອັບພລິເຄຊັນ ແລະ ຂໍ້ມູນຂອງມັນຈະຖືກລຶບອອກຈາກຜູ້ໃຊ້"<b>"ທັງໝົດ"</b>"ໃນອຸປະກອນນີ້."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບນີ້ສຳລັບຜູ້ໃຊ້ <xliff:g id="USERNAME">%1$s</xliff:g> ບໍ່?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບນີ້ຈາກໂປຣໄຟລ໌ບ່ອນເຮັດວຽກບໍ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ແທັບເລັດ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ໂທລະທັດ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ໂຄລນ"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"ເກັບ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ໄວ້ໃນແຟ້ມບໍ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ສືບຕໍ່"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ການຕັ້ງຄ່າ"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"ການຕິດຕັ້ງ/ຖອນການຕິດຕັ້ງແອັບ Wear"</string>
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
index b5ae480..47778ec 100644
--- a/packages/PackageInstaller/res/values-lt/strings.xml
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Šis naudotojas negali diegti nežinomų programų"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Šiam naudotojui neleidžiama diegti programų"</string>
<string name="ok" msgid="7871959885003339302">"Gerai"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archyvuoti"</string>
<string name="update_anyway" msgid="8792432341346261969">"Vis tiek atnaujinti"</string>
<string name="manage_applications" msgid="5400164782453975580">"Tvark. progr."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nėra vietos"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Pašalinti naujinį"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"Veikla „<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>“ yra toliau nurodytos programos dalis."</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Ar norite pašalinti šią programą?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Jūsų asmens duomenys bus išsaugoti"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Archyvuoti šią programą visiems naudotojams? Jūsų asmens duomenys bus išsaugoti"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Archyvuoti šią programą darbo profilyje? Jūsų asmens duomenys bus išsaugoti"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Archyvuoti šią programą naudotojui (-ai) (<xliff:g id="USERNAME">%1$s</xliff:g>)? Jūsų asmens duomenys bus išsaugoti"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ar norite archyvuoti šią programą iš privačios erdvės? Jūsų asmens duomenys bus išsaugoti"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ar norite pašalinti šią programą "<b>"visiems"</b>" naudotojams? Programa ir jos duomenys bus pašalinti "<b>"visiems"</b>" įrenginio naudotojams."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Ar norite pašalinti šią naudotojo <xliff:g id="USERNAME">%1$s</xliff:g> programą?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ar norite pašalinti šią programą iš savo darbo profilio?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Planšetinis kompiuteris ir asmens duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą planšetiniam kompiuteriui arba prarastus duomenis dėl šios programos naudojimo."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV ir asmens duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą TV arba prarastus duomenis dėl šios programos naudojimo."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"„<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“ kopija"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Archyvuoti „<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Tęsti"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Nustatymai"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Įdiegiamos / pašalinamos „Wear“ program."</string>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
index 61b5392..b129706 100644
--- a/packages/PackageInstaller/res/values-lv/strings.xml
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Šis lietotājs nevar instalēt nezināmas lietotnes"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Šim lietotājam nav atļauts instalēt lietotnes"</string>
<string name="ok" msgid="7871959885003339302">"Labi"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arhivēt"</string>
<string name="update_anyway" msgid="8792432341346261969">"Tik un tā atjaunināt"</string>
<string name="manage_applications" msgid="5400164782453975580">"Pārv. lietotnes"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nav brīvas vietas"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Atinstalēt atjauninājumu"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ir daļa no šādas lietotnes:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Vai vēlaties atinstalēt šo lietotni?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Jūsu personas dati tiks saglabāti."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vai arhivēt šo lietotni visiem lietotājiem? Jūsu personas dati tiks saglabāti."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vai arhivēt šo lietotni jūsu darba profilā? Jūsu personas dati tiks saglabāti."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Vai arhivēt šo lietotāja <xliff:g id="USERNAME">%1$s</xliff:g> lietotni? Jūsu personas dati tiks saglabāti."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vai vēlaties arhivēt šo lietotni no savas privātās telpas? Jūsu personas dati tiks saglabāti."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vai vēlaties atinstalēt šo lietotni "<b>"visiem"</b>" lietotājiem? Šī lietojumprogramma un tās dati tiks noņemti no "<b>"visiem"</b>" ierīces lietotāju kontiem."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Vai vēlaties atinstalēt šo lietotni lietotājam <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vai vēlaties atinstalēt šo lietotni no sava darba profila?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Jūsu planšetdators un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par planšetdatora bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Jūsu televizors un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par televizora bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Lietotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klons"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Vai arhivēt lietotni <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Tālāk"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Iestatījumi"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear lietotņu instalēšana/atinstalēšana"</string>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
index 300ef67..f453f57 100644
--- a/packages/PackageInstaller/res/values-mk/strings.xml
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Корисников не може да инсталира непознати апликации"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"На корисников не му е дозволено да инсталира апликации"</string>
<string name="ok" msgid="7871959885003339302">"Во ред"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Архивирај"</string>
<string name="update_anyway" msgid="8792432341346261969">"Сепак ажурирај"</string>
<string name="manage_applications" msgid="5400164782453975580">"Управување со апликациите"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Нема простор"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Деинсталирајте ажурирање"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> е дел од следната апликација:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Дали сакате да ја деинсталирате оваа апликација?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Вашите лични податоци ќе се зачуваат"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Да се архивира апликацијава за сите корисници? Вашите лични податоци ќе се зачуваат"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Да се архивира апликацијава на вашиот работен профил? Вашите лични податоци ќе се зачуваат"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Да се архивира апликацијава за <xliff:g id="USERNAME">%1$s</xliff:g>? Вашите лични податоци ќе се зачуваат"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Дали сакате да ја архивирате апликацијава од вашиот „Приватен простор“? Вашите лични податоци ќе се зачуваат"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Дали сакате да ја деинсталирате оваа апликација за "<b>"сите"</b>" корисници? Апликацијата и нејзините податоци ќе се отстранат од "<b>"сите"</b>" корисници на уредот."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Дали сакате да ја деинсталирате апликацијава за корисникот <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Дали сакате да ја деинсталирате апликацијава од работниот профил?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Таблетот и личните податоци се поподложни на напади од апликации од непознати извори. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на таблетот или загуба на податоци поради нејзиното користење."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Телевизорот и личните податоци се поподложни на напади од апликации од непознати извори. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на телевизорот или загуба на податоци поради нејзиното користење."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Клон на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Да се архивира <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Продолжи"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Поставки"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Се инсталираат/деинсталираат аплик. Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
index 7f071f6..f193acb 100644
--- a/packages/PackageInstaller/res/values-ml/strings.xml
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ഈ ഉപയോക്താവിന്, അജ്ഞാത ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാനാവില്ല"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ ഈ ഉപയോക്താവിന് അനുവാദമില്ല"</string>
<string name="ok" msgid="7871959885003339302">"ശരി"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ആർക്കൈവ് ചെയ്യുക"</string>
<string name="update_anyway" msgid="8792432341346261969">"എന്തായാലും അപ്ഡേറ്റ് ചെയ്യുക"</string>
<string name="manage_applications" msgid="5400164782453975580">"ആപ്പുകൾ മാനേജ് ചെയ്യുക"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ഇടമില്ല"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"അപ്ഡേറ്റ്, അൺ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, ഇനിപ്പറയുന്ന ആപ്പിന്റെ ഭാഗമാണ് :"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ഈ ആപ്പ് അൺ ഇൻസ്റ്റാൾ ചെയ്യണോ?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"നിങ്ങളുടെ വ്യക്തിപരമായ ഡാറ്റ സംരക്ഷിക്കും"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"എല്ലാ ഉപയോക്താക്കൾക്കുമായി ഈ ആപ്പ് ആർക്കൈവ് ചെയ്യണോ? നിങ്ങളുടെ വ്യക്തിപരമായ ഡാറ്റ സംരക്ഷിക്കും"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിൽ ഈ ആപ്പ് ആർക്കൈവ് ചെയ്യണോ? നിങ്ങളുടെ വ്യക്തിപരമായ ഡാറ്റ സംരക്ഷിക്കും"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> എന്നയാൾക്കായി ഈ ആപ്പ് ആർക്കൈവ് ചെയ്യണോ? നിങ്ങളുടെ വ്യക്തിപരമായ ഡാറ്റ സംരക്ഷിക്കും"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"നിങ്ങളുടെ സ്വകാര്യ സ്പേസിൽ നിന്ന് ഈ ആപ്പ് ആർക്കൈവ് ചെയ്യണോ? നിങ്ങളുടെ വ്യക്തിപരമായ ഡാറ്റ സംരക്ഷിക്കും"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ഈ അപ്പ് "<b>"എല്ലാ"</b>" ഉപയോക്താക്കൾക്കുമായി അൺ ഇൻസ്റ്റാൾ ചെയ്യണോ? ഉപകരണത്തിലെ "<b>"എല്ലാ"</b>" ഉപയോക്താക്കളിൽ നിന്നും ആപ്പും അതിന്റെ ഡാറ്റയും നീക്കം ചെയ്യപ്പെടും."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> എന്ന ഉപയോക്താവിനായി ഈ ആപ്പ് അൺ ഇൻസ്റ്റാൾ ചെയ്യണോ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിൽ നിന്ന് ഈ ആപ്പ് അൺ ഇൻസ്റ്റാൾ ചെയ്യണോ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"അജ്ഞാതമായ ആപ്പുകളാൽ നിങ്ങളുടെ ടാബ്ലെറ്റും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ടാബ്ലെറ്റിന് സംഭവിച്ചേക്കാവുന്ന ഏത് നാശനഷ്ടത്തിന്റെയും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്ടത്തിന്റെയും ഉത്തരവാദിത്തം നിങ്ങൾക്കായിരിക്കുമെന്ന് അംഗീകരിക്കുന്നു."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"അജ്ഞാതമായ ആപ്പുകളാൽ നിങ്ങളുടെ ടിവിയും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ടിവിക്ക് സംഭവിച്ചേക്കാവുന്ന ഏത് നാശനഷ്ടത്തിന്റെയും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്ടത്തിന്റെയും ഉത്തരവാദിത്തം നിങ്ങൾക്കായിരിക്കുമെന്ന് അംഗീകരിക്കുന്നു."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ക്ലോൺ ചെയ്യൽ"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ആർക്കൈവ് ചെയ്യണോ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"തുടരുക"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ക്രമീകരണം"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear ആപ്പ് ഇൻസ്റ്റാൾ/അൺ ഇൻസ്റ്റാൾ ചെയ്യുന്നു"</string>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
index 35beb14..091778f 100644
--- a/packages/PackageInstaller/res/values-mn/strings.xml
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Энэ хэрэглэгч тодорхойгүй апп суулгах боломжгүй"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Энэ хэрэглэгч нь апп суулгах зөвшөөрөлгүй байна"</string>
<string name="ok" msgid="7871959885003339302">"ОК"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Архивлах"</string>
<string name="update_anyway" msgid="8792432341346261969">"Ямартай ч шинэчлэх"</string>
<string name="manage_applications" msgid="5400164782453975580">"Аппуудыг удирдах"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Орон зай дутагдаж байна"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Шинэчлэлтийг устгах"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> нь дараах аппын хэсэг болно:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Та энэ аппыг устгахыг хүсэж байна уу?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Таны хувийн өгөгдлийг хадгална"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Энэ аппыг бүх хэрэглэгчид архивлах уу? Таны хувийн өгөгдлийг хадгална"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Энэ аппыг таны ажлын профайлд архивлах уу? Таны хувийн өгөгдлийг хадгална"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Энэ аппыг <xliff:g id="USERNAME">%1$s</xliff:g>-д архивлах уу? Таны хувийн өгөгдлийг хадгална"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Та энэ аппыг хувийн орон зайнаасаа архивлахыг хүсэж байна уу? Таны хувийн өгөгдлийг хадгална"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Та энэ аппыг "<b>"бүх"</b>" хэрэглэгчээс устгахыг хүсэж байна уу? Апп болон доторх өгөгдлийг төхөөрөмж дээрх "<b>"бүх"</b>" хэрэглэгчээс устгана."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Та энэ аппыг <xliff:g id="USERNAME">%1$s</xliff:g> хэрэглэгчийн өмнөөс устгахыг хүсэж байна уу?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Та энэ аппыг ажлын профайлаасаа устгахыг хүсэж байна уу?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Таны таблет болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны таблетад гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Таны ТВ болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны ТВ-д гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клон"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-г архивлах уу?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Үргэлжлүүлэх"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Тохиргоо"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear аппуудыг суулгаж/устгаж байна"</string>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index 44a0e27..a8c9bd0 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"या वापरकर्त्याद्वारे अज्ञात अॅप्स इंस्टॉल केली जाऊ शकत नाहीत"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"या वापरकर्त्याला अॅप्स इंस्टॉल करण्याची अनुमती नाही"</string>
<string name="ok" msgid="7871959885003339302">"ओके"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"संग्रहित करा"</string>
<string name="update_anyway" msgid="8792432341346261969">"तरीही अपडेट करा"</string>
<string name="manage_applications" msgid="5400164782453975580">"अॅप्स व्यवस्थापन"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"जागा संपली"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"अपडेट अनइंस्टॉल करा"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> खालील ॲपचा भाग आहे:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"तुम्हाला हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"तुमचा वैयक्तिक डेटा सेव्ह केला जाईल"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"सर्व वापरकर्त्यांसाठी हे ॲप संग्रहित करायचे आहे का? तुमचा वैयक्तिक डेटा सेव्ह केला जाईल"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"तुमच्या कार्य प्रोफाइलवर हे ॲप संग्रहित करायचे आहे का? तुमचा वैयक्तिक डेटा सेव्ह केला जाईल"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी हे ॲप संग्रहित करायचे आहे का? तुमचा वैयक्तिक डेटा सेव्ह केला जाईल"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"तुम्हाला तुमच्या खाजगी स्पेसमधून हे ॲप संग्रहित करायचे आहे का? तुमचा वैयक्तिक डेटा सेव्ह केला जाईल"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"तुम्हाला हे अॅप "<b>"सर्व"</b>" वापरकर्त्यांसाठी अनइंस्टॉल करायचे आहे का? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील "<b>"सर्व"</b>" वापरकर्त्यांकडून काढला जाईल."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"तुम्हाला <xliff:g id="USERNAME">%1$s</xliff:g> वापरकर्त्यासाठी हे अॅप अनइंस्टॉल करायचे आहे का?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"तुम्हाला तुमच्या कार्य प्रोफाइलमधून हे ॲप अनइंस्टॉल करायचे आहे का?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टॅबलेटचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तुमचा टीव्ही आणि वैयक्तिक डेटा अज्ञात अॅप्सकडून होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हे अॅप इंस्टॉल करून, तुम्ही सहमती देता की ते वापरल्याने तुमच्या टीव्हीचे कोणत्याही प्रकारे होणारे नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोन केलेले"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> संग्रहित करायचे आहे का?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"सुरू ठेवा"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"सेटिंग्ज"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"wear अॅप्स इंस्टॉल/अनइंस्टॉल करत आहे"</string>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
index 489c74e..5b4ed96 100644
--- a/packages/PackageInstaller/res/values-ms/strings.xml
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apl yang tidak diketahui tidak boleh dipasang oleh pengguna ini"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Pengguna ini tidak dibenarkan memasang apl"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arkib"</string>
<string name="update_anyway" msgid="8792432341346261969">"Kemas kinikan juga"</string>
<string name="manage_applications" msgid="5400164782453975580">"Urus apl"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Kehabisan ruang"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Nyahpasang kemas kini"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> merupakan sebahagian daripada apl berikut:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Adakah anda mahu menyahpasang apl ini?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Data peribadi anda akan disimpan"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arkibkan apl ini untuk semua pengguna? Data peribadi anda akan disimpan"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arkibkan apl ini dalam profil kerja anda? Data peribadi anda akan disimpan"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Arkibkan apl ini untuk <xliff:g id="USERNAME">%1$s</xliff:g>? Data peribadi anda akan disimpan"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Adakah anda mahu mengarkibkan apl ini daripada ruang peribadi anda? Data peribadi anda akan disimpan"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Adakah anda mahu menyahpasang apl ini untuk "<b>"semua"</b>" pengguna? Aplikasi dan datanya akan dialih keluar daripada "<b>"semua"</b>" pengguna pada peranti."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Adakah anda ingin menyahpasang apl ini untuk pengguna <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Adakah anda mahu menyahpasang apl ini daripada profil kerja anda?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tablet dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada tablet anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada TV anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Arkibkan <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Teruskan"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Tetapan"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Memasang/menyahpasang apl wear"</string>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
index bc2cb8e..d34d363 100644
--- a/packages/PackageInstaller/res/values-my/strings.xml
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"အရင်းအမြစ်မသိသော အက်ပ်များကို ဤအသုံးပြုသူက ထည့်သွင်းခွင့်မရှိပါ"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ဤအသုံးပြုသူသည် အက်ပ်များကို ထည့်သွင်းခွင့်မရှိပါ"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"သိမ်းရန်"</string>
<string name="update_anyway" msgid="8792432341346261969">"ဘာဖြစ်ဖြစ် အပ်ဒိတ်လုပ်ရန်"</string>
<string name="manage_applications" msgid="5400164782453975580">"အက်ပ်စီမံခြင်း"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"နေရာလွတ်မရှိပါ"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"အပ်ဒိတ်ကို ဖယ်ရှားရန်"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> သည် အောက်ပါအက်ပ်၏ တစ်စိတ်တစ်ဒေသဖြစ်သည်−"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ဤအက်ပ်ကို ဖယ်ရှားလိုပါသလား။"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"သင့်ကိုယ်ရေးအချက်အလက်ကို သိမ်းပါမည်"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ဤအက်ပ်ကို အသုံးပြုသူအားလုံးအတွက် သိမ်းမလား။ သင့်ကိုယ်ရေးအချက်အလက်ကို သိမ်းပါမည်"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ဤအက်ပ်ကို သင့်အလုပ်ပရိုဖိုင်တွင် သိမ်းမလား။ သင့်ကိုယ်ရေးအချက်အလက်ကို သိမ်းပါမည်"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"ဤအက်ပ်ကို <xliff:g id="USERNAME">%1$s</xliff:g> အတွက် သိမ်းမလား။ သင့်ကိုယ်ရေးအချက်အလက်ကို သိမ်းပါမည်"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ဤအက်ပ်ကို သင့်သီးသန့်နေရာတွင် သိမ်းလိုသလား။ သင့်ကိုယ်ရေးအချက်အလက်ကို သိမ်းပါမည်"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ဤအပလီကေးရှင်းကို အသုံးပြုသူ "<b>"အားလုံး"</b>" အတွက် ဖယ်ရှားလိုပါသလား။ ဤအပလီကေးရှင်းနှင့် သက်ဆိုင်ရာ အချက်အလက်များ အားလုံးကို "<b>" က "</b>" စက်အသုံးပြုသူများအတွက် ဖယ်ရှားလိုက်ပါမည်။"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"သင်သည် အသုံးပြုသူ <xliff:g id="USERNAME">%1$s</xliff:g> အတွက် ဤအကောင့်ကို ဖယ်ရှားလိုပါသလား။"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"သင့်အလုပ်ပရိုဖိုင်ကနေ ဤအက်ပ်ကို ဖယ်ရှားလိုတာ သေချာပါသလား။"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"သင်၏ တက်ဘလက်နှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော တက်ဘလက်ပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"သင်၏ TV နှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော TV ပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ပုံတူပွား"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ကို သိမ်းမလား။"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ရှေ့ဆက်ရန်"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ဆက်တင်များ"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"wear အက်ပ်ကိုထည့်သွင်းခြင်း/ဖယ်ရှားခြင်း"</string>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
index 2a24daa..0a5ea5f 100644
--- a/packages/PackageInstaller/res/values-nb/strings.xml
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ukjente apper kan ikke installeres av denne brukeren"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Brukeren har ikke tillatelse til å installere apper"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arkivér"</string>
<string name="update_anyway" msgid="8792432341346261969">"Oppdater likevel"</string>
<string name="manage_applications" msgid="5400164782453975580">"Administrer apper"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Tom for plass"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Avinstaller oppdateringen"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> er del av følgende app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Vil du avinstallere denne appen?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"De personlige dataene dine blir lagret"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vil du arkivere denne appen for alle brukere? De personlige dataene dine blir lagret"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vil du arkivere denne appen på jobbprofilen din? De personlige dataene dine blir lagret"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Vil du arkivere denne appen for <xliff:g id="USERNAME">%1$s</xliff:g>? De personlige dataene dine blir lagret"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vil du arkivere denne appen fra det private området ditt? De personlige dataene dine blir lagret"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vil du avinstallere denne appen for "<b>"alle"</b>" brukere? Appen og tilhørende data blir fjernet fra "<b>"alle"</b>" brukere på enheten."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Ønsker du å avinstallere denne appen for brukeren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vil du avinstallere denne appen fra jobbprofilen din?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Nettbrettet ditt og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på nettbrettet eller tap av data som kan skyldes bruk av appen."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV-en din og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på TV-en eller tap av data som kan skyldes bruk av appen."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-klon"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Vil du arkivere <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsett"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Innstillinger"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Installerer/avinstallerer Wear-apper"</string>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index 7dbe141..6ead922 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"यी प्रयोगकर्ता अज्ञात एपहरू इन्स्टल गर्न सक्नुहुन्न"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"यो प्रयोगकर्तालाई एपहरू इन्स्टल गर्ने अनुमति छैन"</string>
<string name="ok" msgid="7871959885003339302">"ठिक छ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"अभिलेखमा राख्नुहोस्"</string>
<string name="update_anyway" msgid="8792432341346261969">"जे भए पनि अपडेट गर्नुहोस्"</string>
<string name="manage_applications" msgid="5400164782453975580">"एपको प्रबन्ध गर्नु…"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"खाली ठाउँ छैन"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"अद्यावधिकको स्थापना रद्द गर्नु…"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> निम्न एपको अंश हो:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"तपाईं यो एप अनइन्स्टल गर्न चाहनुहुन्छ?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"तपाईंको व्यक्तिगत जानकारी सेभ गरिने छ"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"यो एप सबै प्रयोगकर्ताहरूका लागि अभिलेखमा राख्ने हो? तपाईंको व्यक्तिगत जानकारी सेभ गरिने छ"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"यो एप तपाईंको कार्य प्रोफाइलबाट अभिलेखमा राख्ने हो? तपाईंको व्यक्तिगत जानकारी सेभ गरिने छ"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"यो एप <xliff:g id="USERNAME">%1$s</xliff:g> का लागि अभिलेखमा राख्ने हो? तपाईंको व्यक्तिगत जानकारी सेभ गरिने छ"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"तपाईं आफ्नो निजी स्पेसबाट यो एप अभिलेखमा राख्न चाहनुहुन्छ? तपाईंको व्यक्तिगत जानकारी सेभ गरिने छ"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"तपाईं "<b>"सबै"</b>" प्रयोगकर्ताका लागि यो एप अनइन्स्टल गर्न चाहनुहुन्छ? डिभाइसका "<b>"सबै"</b>" प्रयोगकर्ताहरूबाट उक्त एप र यसको डेटा हटाइने छ।"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"तपाईं प्रयोगकर्ता <xliff:g id="USERNAME">%1$s</xliff:g> का लागि यो एप अनइन्स्टल गर्न चाहनुहुन्छ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"तपाईं आफ्नो कार्य प्रोफाइलबाट यो एप अनइन्स्टल गर्न चाहनुहुन्छ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"तपाईंको ट्याब्लेट तथा व्यक्तिगत डेटा अज्ञात एपहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो एप स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको ट्याब्लेटमा हुन सक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुने कुरामा सहमत हुनुहुन्छ।"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"तपाईंको टिभी तथा व्यक्तिगत डेटा अज्ञात एपहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो एप स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको टिभी मा हुन सक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुने कुरामा सहमत हुनुहुन्छ।"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोन"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> अभिलेखमा राख्ने हो?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"जारी राख्नुहोस्"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"सेटिङहरू"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"वेयर एपहरूको स्थापना/स्थापना रद्द गर्दै"</string>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index 4816bab..d5aac78 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Onbekende apps kunnen niet worden geïnstalleerd door deze gebruiker"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Deze gebruiker mag geen apps installeren"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archiveren"</string>
<string name="update_anyway" msgid="8792432341346261969">"Toch updaten"</string>
<string name="manage_applications" msgid="5400164782453975580">"Apps beheren"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Geen ruimte beschikbaar"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Update verwijderen"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> maakt deel uit van de volgende app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Wil je deze app verwijderen?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Je persoonsgegevens worden opgeslagen"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Deze app archiveren voor alle gebruikers? Je persoonsgegevens worden opgeslagen."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Deze app in je werkprofiel archiveren? Je persoonsgegevens worden opgeslagen."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Deze app voor <xliff:g id="USERNAME">%1$s</xliff:g> archiveren? Je persoonsgegevens worden opgeslagen."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Wil je deze app in je privéruimte archiveren? Je persoonsgegevens worden opgeslagen."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Wil je deze app verwijderen voor "<b>"alle"</b>" gebruikers? Deze app en de gegevens ervan worden verwijderd voor "<b>"alle"</b>" gebruikers van het apparaat."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Wil je deze app verwijderen voor de gebruiker <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Wil je deze app verwijderen uit je werkprofiel?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Je tablet en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tablet of gegevensverlies als gevolg van het gebruik van de app."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Je tv en persoonsgegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tv of gegevensverlies als gevolg van het gebruik van de app."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> archiveren?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Doorgaan"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Instellingen"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear-apps installeren/verwijderen"</string>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
index 70eab2d..a4060de 100644
--- a/packages/PackageInstaller/res/values-or/strings.xml
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ଏହି ୟୁଜରଙ୍କ ଦ୍ୱାରା ଅଜଣା ଆପ୍ ଇନଷ୍ଟଲ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ଏହି ୟୁଜର୍ ଆପ୍ ଇନଷ୍ଟଲ୍ କରିପାରିବେ ନାହିଁ"</string>
<string name="ok" msgid="7871959885003339302">"ଠିକ୍ ଅଛି"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ଆର୍କାଇଭ କରନ୍ତୁ"</string>
<string name="update_anyway" msgid="8792432341346261969">"ଯେ କୌଣସି ମତେ ଅପଡେଟ କରନ୍ତୁ"</string>
<string name="manage_applications" msgid="5400164782453975580">"ଆପ୍ଗୁଡ଼ିକର ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ଆଉ ସ୍ଥାନ ନାହିଁ"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"ଅପଡେଟ୍ ଅନଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ହେଉଛି ନିମ୍ନ ଆପ୍ର ଏକ ଅଂଶ।"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ଆପଣ ଏହି ଆପ୍ ଅନଇନଷ୍ଟଲ୍ କରିବାକୁ ଚାହାଁନ୍ତି କି?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଡାଟା ସେଭ ହୋଇଯିବ"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ସମସ୍ତ ୟୁଜରଙ୍କ ପାଇଁ ଏହି ଆପକୁ ଆର୍କାଇଭ କରିବେ? ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଡାଟା ସେଭ ହୋଇଯିବ"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲରେ ଏହି ଆପକୁ ଆର୍କାଇଭ କରିବେ? ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଡାଟା ସେଭ ହୋଇଯିବ"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ଏହି ଆପକୁ ଆର୍କାଇଭ କରିବେ? ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଡାଟା ସେଭ ହୋଇଯିବ"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ଆପଣ ଆପଣଙ୍କ ପ୍ରାଇଭେଟ ସ୍ପେସରୁ ଏହି ଆପକୁ ଆର୍କାଇଭ କରିବାକୁ ଚାହାଁନ୍ତି? ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଡାଟା ସେଭ ହୋଇଯିବ"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ଆପଣ "<b>"ସମସ୍ତ"</b>" ୟୁଜର୍ଙ୍କ ପାଇଁ ଏହି ଆପ୍କୁ ଅନଷ୍ଟଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଡିଭାଇସ୍ରେ ଥିବା "<b>"ସମସ୍ତ"</b>" ୟୁଜର୍ ଆପ୍ଲିକେଶନ୍ ଏବଂ ତାହାର ଡାଟା ବାହାର କରିଦିଆଯିବ।"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"ୟୁଜର୍ <xliff:g id="USERNAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ଆପଣ ଏହି ଆପ୍ ଇନଷ୍ଟଲ୍ କରିବେ କି?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ଆପଣ ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲରୁ ଏହି ଆପକୁ ଅନଇନଷ୍ଟଲ କରିବାକୁ ଚାହାଁନ୍ତି କି?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ଅଜଣା ଆପ୍ ଦ୍ୱାରା ଆପଣଙ୍କ ଟାବଲେଟ୍ ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପ୍କୁ ଇନଷ୍ଟଲ୍ କରିବାର ଅର୍ଥ ହେଉଛି ଆପଣଙ୍କ ଟାବ୍ଲେଟ୍ରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ଅଜଣା ଆପ୍ ଦ୍ୱାରା ଆପଣଙ୍କ ଟିଭି ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପ୍କୁ ଇନଷ୍ଟଲ୍ କରିବାର ଅର୍ଥ ହେଉଛି ଆପଣଙ୍କ ଟିଭିରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> କ୍ଲୋନ"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>କୁ ଆର୍କାଇଭ କରିବେ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ଜାରି ରଖନ୍ତୁ"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ସେଟିଂସ"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"ୱିଅର୍ ଆପ୍ ଇନଷ୍ଟଲ୍/ଅନଇନଷ୍ଟଲ୍ କରାଯାଉଛି"</string>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index d91d032..c54d93f 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ਇਹ ਵਰਤੋਂਕਾਰ ਅਗਿਆਤ ਐਪਾਂ ਨੂੰ ਸਥਾਪਤ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
<string name="ok" msgid="7871959885003339302">"ਠੀਕ ਹੈ"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ਪੁਰਾਲੇਖਬੱਧ ਕਰੋ"</string>
<string name="update_anyway" msgid="8792432341346261969">"ਫਿਰ ਵੀ ਅੱਪਡੇਟ ਕਰੋ"</string>
<string name="manage_applications" msgid="5400164782453975580">"ਐਪਾਂ ਪ੍ਰਬੰਧਿਤ ਕਰੋ"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ਜਗ੍ਹਾ ਖਾਲੀ ਨਹੀਂ"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"ਅੱਪਡੇਟ ਅਣਸਥਾਪਤ ਕਰੋ"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ਅੱਗੇ ਦਿੱਤੀ ਐਪ ਦਾ ਭਾਗ ਹੈ:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਨਿੱਜੀ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ \'ਤੇ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਵਿੰਨਣਸ਼ੀਲ ਹਨ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਟੈਬਲੈੱਟ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ ਡਾਟੇ ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ੁੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ਤੁਹਾਡਾ ਟੀਵੀ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਵਿੰਨਣਸ਼ੀਲ ਹਨ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਟੀਵੀ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ ਡਾਟੇ ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ੁੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਦਾ ਕਲੋਨ"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"ਕੀ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਨੂੰ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ਜਾਰੀ ਰੱਖੋ"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ਸੈਟਿੰਗਾਂ"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"ਵੀਅਰ ਐਪਾਂ ਸਥਾਪਤ ਜਾਂ ਅਣਸਥਾਪਤ ਕਰਨਾ"</string>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index 41bec16..ec9ac15 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ten użytkownik nie może instalować nieznanych aplikacji"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ten użytkownik nie może instalować aplikacji"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archiwizuj"</string>
<string name="update_anyway" msgid="8792432341346261969">"Zaktualizuj mimo to"</string>
<string name="manage_applications" msgid="5400164782453975580">"Zarządzaj aplikacjami"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Brak miejsca"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Odinstaluj aktualizację"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> jest częścią następującej aplikacji:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Odinstalować tę aplikację?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Twoje dane osobiste zostaną zapisane"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Zarchiwizować tę aplikację dla wszystkich użytkowników? Twoje dane osobiste zostaną zapisane"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Zarchiwizować tę aplikację w profilu służbowym? Twoje dane osobiste zostaną zapisane"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Zarchiwizować tę aplikację dla użytkownika <xliff:g id="USERNAME">%1$s</xliff:g>? Twoje dane osobiste zostaną zapisane"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Chcesz zarchiwizować tę aplikację ze swojego obszaru prywatnego? Twoje dane osobiste zostaną zapisane"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcesz odinstalować tę aplikację dla "<b>"wszystkich"</b>" użytkowników? Ta aplikacja i jej dane zostaną usunięte dla "<b>"wszystkich"</b>" użytkowników na urządzeniu."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Chcesz odinstalować tę aplikację dla użytkownika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Czy chcesz odinstalować tę aplikację z profilu służbowego?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Dane na tablecie i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie tabletu lub utratę danych w wyniku jej używania."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Dane na telewizorze i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie telewizora lub utratę danych w wyniku jej używania."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikacji <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Zarchiwizować aplikację <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Dalej"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ustawienia"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalacja/usuwanie aplikacji na Wear"</string>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index 9d943a5..9c540e7 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arquivar"</string>
<string name="update_anyway" msgid="8792432341346261969">"Atualizar mesmo assim"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gerenciar apps"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sem espaço"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar atualização"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Você quer desinstalar este app?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arquivar esse app para todos os usuários? Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arquivar esse app no seu perfil de trabalho? Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Arquivar esse app para <xliff:g id="USERNAME">%1$s</xliff:g>? Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Você quer desinstalar esse app do seu espaço particular? Seus dados pessoais serão salvos"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O aplicativo e os dados dele serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Você quer desinstalar esse app do seu perfil de trabalho?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Sua TV e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Arquivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Configurações"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps do Wear"</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index 9d943a5..9c540e7 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arquivar"</string>
<string name="update_anyway" msgid="8792432341346261969">"Atualizar mesmo assim"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gerenciar apps"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sem espaço"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar atualização"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> é parte do seguinte app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Você quer desinstalar este app?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arquivar esse app para todos os usuários? Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arquivar esse app no seu perfil de trabalho? Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Arquivar esse app para <xliff:g id="USERNAME">%1$s</xliff:g>? Seus dados pessoais serão salvos"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Você quer desinstalar esse app do seu espaço particular? Seus dados pessoais serão salvos"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Quer desinstalar este app para "<b>"todos"</b>" os usuários? O aplicativo e os dados dele serão removidos para "<b>"todos"</b>" os usuários do dispositivo."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Quer desinstalar este app para o usuário <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Você quer desinstalar esse app do seu perfil de trabalho?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Sua TV e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer perda de dados ou dano ao dispositivo causados pelo uso desses apps."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Arquivar <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuar"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Configurações"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalando/desinstalando apps do Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index cf50304..0641370 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplicațiile necunoscute nu pot fi instalate de acest utilizator"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Acest utilizator nu are permisiunea să instaleze aplicații"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arhivează"</string>
<string name="update_anyway" msgid="8792432341346261969">"Actualizează oricum"</string>
<string name="manage_applications" msgid="5400164782453975580">"Gestionează"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Spațiu de stocare insuficient"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalează actualizarea"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> face parte din următoarea aplicație:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Dezinstalezi această aplicație?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Datele tale cu caracter personal vor fi salvate"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Arhivezi aplicația pentru toți utilizatorii? Datele tale cu caracter personal vor fi salvate"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Arhivezi aplicația din profilul de serviciu? Datele tale cu caracter personal vor fi salvate"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Arhivezi aplicația pentru <xliff:g id="USERNAME">%1$s</xliff:g>? Datele tale cu caracter personal vor fi salvate"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Arhivezi aplicația din spațiul privat? Datele tale cu caracter personal vor fi salvate"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Dezinstalezi această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalezi această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Dezinstalezi această aplicație din profilul de serviciu?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi aplicația, accepți că ești singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele tale cu caracter personal sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clonează <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Arhivezi <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Continuă"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Setări"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Se (dez)instalează aplicațiile Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
index 2a85a0c..17222d1 100644
--- a/packages/PackageInstaller/res/values-ru/strings.xml
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Этот пользователь не может устанавливать неизвестные приложения."</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Этому пользователю не разрешено устанавливать приложения."</string>
<string name="ok" msgid="7871959885003339302">"ОК"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Отправить в архив"</string>
<string name="update_anyway" msgid="8792432341346261969">"Все равно обновить"</string>
<string name="manage_applications" msgid="5400164782453975580">"Управление приложениями"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Недостаточно места"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Удалить обновление"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> – часть следующего приложения:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Удалить приложение?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Персональные данные будут сохранены."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Отправить это приложение в архив для всех пользователей? Персональные данные будут сохранены."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Отправить в архив это приложение из рабочего профиля? Персональные данные будут сохранены."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Отправить это приложение в архив для пользователя <xliff:g id="USERNAME">%1$s</xliff:g>? Персональные данные будут сохранены."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Отправить в архив это приложение из личного пространства? Персональные данные будут сохранены."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Удалить это приложение для "<b>"всех"</b>" пользователей устройства? Они потеряют доступ как к приложению, так и к связанным с ним данным."<b></b></string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Удалить это приложение из профиля <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Удалить это приложение из рабочего профиля?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ваши персональные данные и данные планшета более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы берете на себя всю ответственность за последствия, связанные с его использованием, то есть за любой ущерб, нанесенный планшету, и возможную потерю данных."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Ваши персональные данные и данные телевизора более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы берете на себя всю ответственность за последствия, связанные с его использованием, то есть за любой ущерб, нанесенный телевизору, и возможную потерю данных."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Клон приложения <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Отправить в архив приложение \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\"?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Продолжить"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Настройки"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Установка/удаление прилож. для Wear OS"</string>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
index ae94d48..8ba36b8 100644
--- a/packages/PackageInstaller/res/values-si/strings.xml
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"මෙම පරිශීලකයා මඟින් නොදන්නා යෙදුම් ස්ථාපනය කළ නොහැක"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"මෙම පරිශීලකයාට යෙදුම් ස්ථාපනය කිරීමට අවසර නැත"</string>
<string name="ok" msgid="7871959885003339302">"හරි"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ලේඛනාරක්ෂණය"</string>
<string name="update_anyway" msgid="8792432341346261969">"කෙසේ වෙතත් යාවත්කාලීන කරන්න"</string>
<string name="manage_applications" msgid="5400164782453975580">"යෙදුම් කළමනාකරණය කරන්න"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ඉඩ නොමැත"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"යාවත්කාලිනය අස්ථාපනය කරන්න"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> පහත යෙදුමේ කොටසකි:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ඔබට මෙම යෙදුම අස්ථාපනය කිරීමට අවශ්යද?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"ඔබේ පුද්ගලික දත්ත සුරැකෙනු ඇත"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"සියලු පරිශීලකයින් සඳහා මෙම යෙදුම ලේඛනාරක්ෂණයකරන්න ද? ඔබේ පුද්ගලික දත්ත සුරැකෙනු ඇත"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"මෙම යෙදුම ඔබේ කාර්යාල පැතිකඩෙහි ලේඛනාරක්ෂණය කරන්න ද? ඔබේ පුද්ගලික දත්ත සුරැකෙනු ඇත"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> සඳහා මෙම යෙදුම ලේඛනාරක්ෂණය කරන්න ද? ඔබේ පුද්ගලික දත්ත සුරැකෙනු ඇත"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ඔබට ඔබේ පෞද්ගලික අවකාශයෙන් මෙම යෙදුම ලේඛනාරක්ෂණය කිරීමට අවශ්ය ද? ඔබේ පුද්ගලික දත්ත සුරැකෙනු ඇත"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119"><b>"සියලු"</b>" පරිශීලකයන් සඳහා මෙම යෙදුම අස්ථාපනය කිරීමට ඔබට අවශ්යද? උපාංගයෙහි "<b>"සියලු"</b>" පරිශීලකයන් සඳහා යෙදුම සහ එහි දත්ත ඉවත්වනු ඇත."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> පරිශීලකයා සඳහා මෙම යෙදුම අස්ථාපනය කිරීමට ඔබට අවශ්යයද?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ඔබට කාර්යාල පැතිකඩ වෙතින් මෙම යෙදුම අස්ථාපනය කිරීමට අවශ්යද?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"ඔබගේ ටැබ්ලට් පරිගණකය සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ ටැබ්ලට් පරිගණකය සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ඔබගේ TV සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ TV සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ක්ලෝනය"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ලේඛනාරක්ෂණය කරන්න ද?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ඉදිරියට යන්න"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"සැකසීම්"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear යෙදුම් ස්ථාපනය/අස්ථාපනය කරමින්"</string>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
index 53d4a84..e2428cf 100644
--- a/packages/PackageInstaller/res/values-sk/strings.xml
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Tento používateľ nemôže inštalovať neznáme aplikácie"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Tento používateľ nemá povolené inštalovať aplikácie"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Archivovať"</string>
<string name="update_anyway" msgid="8792432341346261969">"Napriek tomu aktualizovať"</string>
<string name="manage_applications" msgid="5400164782453975580">"Spravovať aplikácie"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nedostatok miesta"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Odinštalovať aktualizáciu"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"Aktivita <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je súčasťou nasledujúcej aplikácie:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Chcete túto aplikáciu odinštalovať?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Vaše osobné údaje budú uložené"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Chcete túto aplikáciu archivovať pre všetkých používateľov? Vaše osobné údaje budú uložené."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Chcete túto aplikáciu archivovať vo svojom pracovnom profile? Vaše osobné údaje budú uložené."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Chcete túto aplikáciu archivovať pre použíateľa <xliff:g id="USERNAME">%1$s</xliff:g>? Vaše osobné údaje budú uložené."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Chcete túto aplikáciu archivovať zo svojho súkromného priestoru? Vaše osobné údaje budú uložené."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcete odinštalovať túto aplikáciu pre "<b>"všetkých"</b>" používateľov? Aplikácia a jej údaje sa odstránia z tohto zariadenia pre "<b>"všetkých"</b>" používateľov."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Chcete túto aplikáciu odinštalovať pre používateľa <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Chcete túto aplikáciu odinštalovať zo svojho pracovného profilu?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Váš tablet a osobné dáta sú náchylnejšie na útok z neznámych aplikácií. Inštaláciou tejto aplikácie vyjadrujete súhlas s tým, že nesiete zodpovednosť za akékoľvek poškodenie tabletu alebo stratu dát, ktoré by mohli nastať pri jej používaní."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Váš televízor a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštaláciou tejto aplikácie vyjadrujete súhlas s tým, že nesiete zodpovednosť za akékoľvek poškodenie televízora alebo stratu údajov, ktoré by mohli nastať pri jej používaní."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon aplikácie <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Chcete archivovať <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Pokračovať"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Nastavenia"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Inštalácia/odinštalovanie aplikácií Wear"</string>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
index 2f6b697..a172dab 100644
--- a/packages/PackageInstaller/res/values-sl/strings.xml
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Ta uporabnik nima dovoljenja za nameščanje neznanih aplikacij"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ta uporabnik nima dovoljenja za nameščanje aplikacij"</string>
<string name="ok" msgid="7871959885003339302">"V redu"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arhiviranje"</string>
<string name="update_anyway" msgid="8792432341346261969">"Vseeno posodobi"</string>
<string name="manage_applications" msgid="5400164782453975580">"Upravlj. aplik."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Zmanjkalo je prostora"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Odstrani posodobitev"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> je del te aplikacije:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Ali želite odmestiti to aplikacijo?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Vaši osebni podatki bodo shranjeni."</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ali želite to aplikacijo arhivirati za vse uporabnike? Vaši osebni podatki bodo shranjeni."</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ali želite to aplikacijo arhivirati v delovnem profilu? Vaši osebni podatki bodo shranjeni."</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Ali želite arhivirati to aplikacijo za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>? Vaši osebni podatki bodo shranjeni."</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ali želite arhivirati to aplikacijo iz zasebnega prostora? Vaši osebni podatki bodo shranjeni."</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ali želite odstraniti aplikacijo za "<b>"vse"</b>" uporabnike? Aplikacija in njeni podatki bodo odstranjeni iz "<b>"vseh"</b>" uporabnikov v napravi."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Ali želite to aplikacijo odstraniti za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ali želite to aplikacijo odmestiti iz delovnega profila?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Neznane aplikacije lahko resno ogrozijo varnost tabličnega računalnika in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v tabličnem računalniku, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Neznane aplikacije lahko resno ogrozijo varnost televizorja in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v televizorju, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klonirana aplikacija <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Ali želite arhivirati paket <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Naprej"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Nastavitve"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Nameščanje/odstranjev. aplikacij za Wear"</string>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
index 1c4b354..f0bb589 100644
--- a/packages/PackageInstaller/res/values-sq/strings.xml
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplikacionet e panjohura nuk mund të instalohen nga ky përdorues"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Ky përdorues nuk lejohet të instalojë aplikacione"</string>
<string name="ok" msgid="7871959885003339302">"Në rregull"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arkivo"</string>
<string name="update_anyway" msgid="8792432341346261969">"Përditësoje gjithsesi"</string>
<string name="manage_applications" msgid="5400164782453975580">"Menaxho aplikacionet"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nuk ka hapësirë"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Çinstalo përditësimin"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> është pjesë e aplikacionit të mëposhtëm:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Dëshiron ta çinstalosh këtë aplikacion?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Të dhënat e tua personale do të ruhen"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Të arkivohet ky aplikacion për të gjithë përdoruesit? Të dhënat e tua personale do të ruhen"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Të arkivohet ky aplikacion në profilin tënd të punës? Të dhënat e tua personale do të ruhen"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Të arkivohet ky aplikacion për <xliff:g id="USERNAME">%1$s</xliff:g>? Të dhënat e tua personale do të ruhen"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Dëshiron ta arkivosh këtë aplikacion nga hapësira jote private? Të dhënat e tua personale do të ruhen"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Dëshiron ta çinstalosh këtë aplikacion për "<b>"të gjithë"</b>" përdoruesit? Aplikacioni dhe të dhënat e tij do të hiqen nga "<b>"të gjithë"</b>" përdoruesit e pajisjes."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Dëshiron ta çinstalosh këtë aplikacion për përdoruesin <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Dëshiron ta çinstalosh këtë aplikacion nga profili yt i punës?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableti dhe të dhënat e tua personale janë më të cenueshme nga sulmet nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj tabletit tënd ose çdo humbje të të dhënave që mund të rezultojë nga përdorimi i tij."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizori dhe të dhënat e tua personale janë më të cenueshme nga sulmet nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj televizorit tënd ose çdo humbje të të dhënave që mund të rezultojë nga përdorimi i tij."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon i <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Të arkivohet <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Vazhdo"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Cilësimet"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Instalimi/çinstalimi i aplikacioneve të Wear"</string>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
index 4402d7f..57dce51 100644
--- a/packages/PackageInstaller/res/values-sr/strings.xml
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Овај корисник не може да инсталира непознате апликације"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Овом кориснику није дозвољено да инсталира апликације"</string>
<string name="ok" msgid="7871959885003339302">"Потврди"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Архивирај"</string>
<string name="update_anyway" msgid="8792432341346261969">"Ипак ажурирај"</string>
<string name="manage_applications" msgid="5400164782453975580">"Управљајте апл."</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Нема више простора"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Деинсталирај ажурирање"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> је део следеће апликације:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Желите да деинсталирате ову апликацију?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Лични подаци ће бити сачувани"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Желите ли да архивирате ову апликацију за све кориснике? Лични подаци ће бити сачувани"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Желите ли да архивирате ову апликацију са пословног профила? Лични подаци ће бити сачувани"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Желите ли да архивирате ову апликацију за корисника <xliff:g id="USERNAME">%1$s</xliff:g>? Лични подаци ће бити сачувани"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Желите ли да архивирате ову апликацију из приватног простора? Лични подаци ће бити сачувани"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Да ли желите да деинсталирате ову апликацију за "<b>"све"</b>" кориснике? Апликација и подаци уз ње биће уклоњени за "<b>"све"</b>" кориснике овог уређаја."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Желите ли да деинсталирате ову апликацију за корисника <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Да ли желите да деинсталирате ову апликацију са пословног профила?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Таблет и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења таблета или губитак података до којих може да дође због њеног коришћења."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ТВ и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења ТВ-а или губитак података до којих може да дође због њеног коришћења."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Клон апликације <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Желите ли да архивирате <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Настави"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Подешавања"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Инсталирање/деинсталирање Wear апликац."</string>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index ed016a8..d06f040 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Denna användare får inte installera okända appar"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Användaren har inte behörighet att installera appar"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arkivera"</string>
<string name="update_anyway" msgid="8792432341346261969">"Uppdatera ändå"</string>
<string name="manage_applications" msgid="5400164782453975580">"Hantera appar"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Slut på utrymme"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Avinstallera uppdatering"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> är en del av följande app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Vill du avinstallera appen?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Din privata data sparas"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Vill du arkivera appen för alla användare? Din privata data sparas"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Vill du arkivera appen i din jobbprofil? Din privata data sparas"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Vill du arkivera appen för <xliff:g id="USERNAME">%1$s</xliff:g>? Din privata data sparas"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Vill du arkivera appen från ditt privata rum? Din privata data sparas"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Vill du avinstallera den här appen för "<b>"alla"</b>" användare? Appen och alla data i den tas bort från "<b>"alla"</b>" användare på enheten."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Vill du avinstallera appen för användaren <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Vill du avinstallera appen från jobbprofilen?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Din surfplatta och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på surfplattan och för dataförlust som kan uppstå vid användning av denna app."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Din tv och personliga data är mer sårbar för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på tv:n och för dataförlust som kan uppstå vid användning av denna app."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Klon av <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Vill du arkivera <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Fortsätt"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Inställningar"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear-appar installeras/avinstalleras"</string>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
index 7f03d91..49af8fc 100644
--- a/packages/PackageInstaller/res/values-sw/strings.xml
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Mtumiaji huyu hana idhini ya kusakinisha programu ambazo hazijulikani"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Mtumiaji huyu haruhusiwi kusakinisha programu"</string>
<string name="ok" msgid="7871959885003339302">"Sawa"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Hifadhi kwenye kumbukumbu"</string>
<string name="update_anyway" msgid="8792432341346261969">"Sasisha tu"</string>
<string name="manage_applications" msgid="5400164782453975580">"Dhibiti programu"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Nafasi imejaa"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Ondoa sasisho"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ni sehemu ya programu ifuatayo:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Ungependa kuondoa programu hii?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Data yako binafsi itahifadhiwa"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Ungependa kuweka programu hii kwenye kumbukumbu kwa watumiaji wote? Data yako binafsi itahifadhiwa"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ungependa kuweka programu hii kwenye kumbukumbu katika wasifu wako wa kazini? Data yako binafsi itahifadhiwa"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Ungependa kuweka programu hii ya <xliff:g id="USERNAME">%1$s</xliff:g> kwenye kumbukumbu? Data yako binafsi itahifadhiwa"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Je, ungependa kuweka programu hii kwenye kumbukumbu kutoka kwenye sehemu yako ya faragha? Data yako binafsi itahifadhiwa"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Je, ungependa kuondoa programu hii kwa watumiaji "<b>"wote"</b>"? Programu na data yake zitaondolewa kutoka kwa watumiaji "<b>"wote"</b>" kwenye kifaa."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Je, ungependa kuondoa programu hii kwa mtumiaji <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ungependa kuondoa programu hii kwenye wasifu wako wa kazini?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Data yako ya binafsi na ya kompyuta yako kibao inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibikia uharibifu wowote kwenye kompyuta yako kibao au kupotea kwa data kutokana na matumizi yake."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Data yako ya binafsi na ya televisheni yako inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibikia uharibifu wowote kwenye televisheni yako au kupotea kwa data kutokana na matumizi yake."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Nakala ya <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Ungependa kuweka <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kwenye kumbukumbu?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Endelea"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Mipangilio"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Inasakinisha/inaondoa programu za Android Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
index 3c98411..865781f 100644
--- a/packages/PackageInstaller/res/values-ta/strings.xml
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"அறியப்படாத ஆப்ஸை இந்தப் பயனரால் நிறுவ இயலாது"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ஆப்ஸை நிறுவ இந்தப் பயனருக்கு அனுமதியில்லை"</string>
<string name="ok" msgid="7871959885003339302">"சரி"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"காப்பிடு"</string>
<string name="update_anyway" msgid="8792432341346261969">"பரவாயில்லை, புதுப்பிக்கவும்"</string>
<string name="manage_applications" msgid="5400164782453975580">"ஆப்ஸை நிர்வகி"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"போதுமான சேமிப்பிடம் இல்லை"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"புதுப்பிப்பை நிறுவல் நீக்குதல்"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> என்பது பின்வரும் ஆப்ஸின் பகுதியாகும்:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"இந்த ஆப்ஸை நிறுவல் நீக்க விரும்புகிறீர்களா?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"உங்கள் தனிப்பட்ட தரவு சேமிக்கப்படும்"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"அனைத்துப் பயனர்களுக்கும் இந்த ஆப்ஸைக் காப்பிடவா? உங்கள் தனிப்பட்ட தரவு சேமிக்கப்படும்"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"உங்கள் பணிக் கணக்கில் இந்த ஆப்ஸைக் காப்பிடவா? உங்கள் தனிப்பட்ட தரவு சேமிக்கப்படும்"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>க்கு இந்த ஆப்ஸைக் காப்பிடவா? உங்கள் தனிப்பட்ட தரவு சேமிக்கப்படும்"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"உங்கள் தனிப்பட்ட சேமிப்பிடத்திலிருந்து இந்த ஆப்ஸைக் காப்பிட வேண்டுமா? உங்கள் தனிப்பட்ட தரவு சேமிக்கப்படும்"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"இந்த ஆப்ஸை "<b>"அனைத்துப்"</b>" பயனர்களுக்கும் நிறுவல் நீக்க விரும்புகிறீர்களா? ஆப்ஸும் அதன் தரவும் சாதனத்திலுள்ள "<b>"அனைத்துப்"</b>" பயனர்களிடமிருந்தும் அகற்றப்படும்."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> என்ற பயனருக்கு இந்த ஆப்ஸை நிறுவல் நீக்க விரும்புகிறீர்களா?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"உங்கள் பணிக் கணக்கிலிருந்து இந்த ஆப்ஸை நிறுவல் நீக்க விரும்புகிறீர்களா?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"அறியப்படாத ஆப்ஸால் உங்கள் டேப்லெட்டும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகலாம். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது டேப்லெட்டில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"அறியப்படாத ஆப்ஸால் உங்கள் டிவியும் தனிப்பட்ட தரவும் மிக எளிதாகப் பாதிப்புக்குள்ளாகலாம். இந்த ஆப்ஸை நிறுவுவதன் மூலம், இதைப் பயன்படுத்தும்போது டிவியில் ஏதேனும் சிக்கல் ஏற்பட்டாலோ உங்களது தரவை இழந்தாலோ அதற்கு நீங்களே பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> குளோன்"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ஐக் காப்பிடவா?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"தொடர்க"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"அமைப்புகள்"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear ஆப்ஸை நிறுவுதல்/நிறுவல் நீக்குதல்"</string>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index ef00c9f..2deac93 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ఈ వినియోగదారు తెలియని యాప్లను ఇన్స్టాల్ చేయలేరు"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"యాప్లను ఇన్స్టాల్ చేయడానికి ఈ వినియోగదారుకు అనుమతి లేదు"</string>
<string name="ok" msgid="7871959885003339302">"సరే"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"ఆర్కైవ్ చేయండి"</string>
<string name="update_anyway" msgid="8792432341346261969">"ఏదేమైనా అప్డేట్ చేయండి"</string>
<string name="manage_applications" msgid="5400164782453975580">"యాప్లను నిర్వహించండి"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ఖాళీ లేదు"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"అప్డేట్ అన్ఇన్స్టాల్ చేయి"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది కింది యాప్లో ఒక భాగం:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"మీరు ఈ యాప్ను అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"మీ వ్యక్తిగత డేటా సేవ్ చేయబడుతుంది"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"యూజర్లందరికీ ఈ యాప్ను ఆర్కైవ్ చేయాలా? మీ వ్యక్తిగత డేటా సేవ్ చేయబడుతుంది"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"మీ వర్క్ ప్రొఫైల్లో ఈ యాప్ను ఆర్కైవ్ చేయాలా? మీ వ్యక్తిగత డేటా సేవ్ చేయబడుతుంది"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g>కు ఈ యాప్ను ఆర్కైవ్ చేయాలా? మీ వ్యక్తిగత డేటా సేవ్ చేయబడుతుంది"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"మీరు మీ ప్రైవేట్ స్పేస్ నుండి ఈ యాప్ను ఆర్కైవ్ చేయాలనుకుంటున్నారా? మీ వ్యక్తిగత డేటా సేవ్ చేయబడుతుంది"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"మీరు ఈ యాప్ను "<b>"అందరు"</b>" వినియోగదారులకు అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా? అప్లికేషన్, దాని డేటా పరికరంలోని "<b>"అందరు"</b>" వినియోగదారుల నుండి తీసివేయబడుతుంది."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"మీరు వినియోగదారు <xliff:g id="USERNAME">%1$s</xliff:g> కోసం ఈ యాప్ను అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"మీ వర్క్ ప్రొఫైల్ నుండి ఈ యాప్ను మీరు అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"మీ టాబ్లెట్ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టాబ్లెట్కు ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"మీ టీవీ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్ను ఇన్స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టీవీకి ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> క్లోన్"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ను ఆర్కైవ్ చేయాలా?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"కొనసాగండి"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"సెట్టింగ్లు"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear యాప్లను ఇన్స్టాల్/అన్ఇన్స్టాల్ చేస్తోంది"</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index e330985..e770a1d 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"ผู้ใช้รายนี้ไม่สามารถติดตั้งแอปที่ไม่รู้จัก"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"ผู้ใช้รายนี้ไม่ได้รับอนุญาตให้ติดตั้งแอป"</string>
<string name="ok" msgid="7871959885003339302">"ตกลง"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"เก็บ"</string>
<string name="update_anyway" msgid="8792432341346261969">"อัปเดตเลย"</string>
<string name="manage_applications" msgid="5400164782453975580">"จัดการแอป"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"ไม่มีพื้นที่"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"ถอนการติดตั้งการอัปเดต"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> เป็นส่วนหนึ่งของแอปต่อไปนี้"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"ต้องการถอนการติดตั้งแอปนี้ไหม"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"ระบบจะบันทึกข้อมูลส่วนตัวของคุณไว้"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"เก็บแอปนี้สำหรับผู้ใช้ทุกคนไหม ระบบจะบันทึกข้อมูลส่วนตัวของคุณไว้"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"เก็บแอปนี้ในโปรไฟล์งานไหม ระบบจะบันทึกข้อมูลส่วนตัวของคุณไว้"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"เก็บแอปนี้ไปสำหรับ <xliff:g id="USERNAME">%1$s</xliff:g> ไหม ระบบจะบันทึกข้อมูลส่วนตัวของคุณไว้"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"คุณต้องการเก็บแอปนี้ไปจากพื้นที่ส่วนตัวไหม ระบบจะบันทึกข้อมูลส่วนตัวของคุณไว้"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ต้องการถอนการติดตั้งแอปนี้สำหรับผู้ใช้"<b>"ทั้งหมด"</b>"ไหม ระบบจะนำแอปและข้อมูลในแอปออกจากผู้ใช้"<b>"ทั้งหมด"</b>"ที่อยู่ในอุปกรณ์"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"ต้องการถอนการติดตั้งแอปนี้สำหรับผู้ใช้ <xliff:g id="USERNAME">%1$s</xliff:g> ไหม"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"คุณต้องการถอนการติดตั้งแอปนี้จากโปรไฟล์งานใช่ไหม"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"แท็บเล็ตและข้อมูลส่วนตัวของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้แสดงว่าคุณยอมรับว่าจะรับผิดชอบต่อความเสียหายใดๆ ที่จะเกิดขึ้นกับแท็บเล็ตหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"ทีวีและข้อมูลส่วนตัวของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้แสดงว่าคุณยอมรับว่าจะรับผิดชอบต่อความเสียหายใดๆ ที่จะเกิดขึ้นกับทีวีหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"โคลนของ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"เก็บ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ไหม"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"ดำเนินการต่อ"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"การตั้งค่า"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"กำลังติดตั้ง/ถอนการติดตั้งแอป Wear"</string>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
index e7ef36b..e654130 100644
--- a/packages/PackageInstaller/res/values-tl/strings.xml
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Hindi maaaring mag-install ang user na ito ng mga hindi kilalang app"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Hindi pinapayagan ang user na ito na mag-install ng mga app"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"I-archive"</string>
<string name="update_anyway" msgid="8792432341346261969">"I-update pa rin"</string>
<string name="manage_applications" msgid="5400164782453975580">"Pamahalaan ang app"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Wala nang espasyo"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"I-uninstall ang update"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"Bahagi ang <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ng sumusunod na app:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Gusto mo bang i-uninstall ang app na ito?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Ise-save ang iyong personal na data"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"I-archive ang app na ito para sa lahat ng user? Ise-save ang iyong personal na data"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"I-archive ang app na ito sa iyong profile sa trabaho? Ise-save ang iyong personal na data"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"I-archive ang app na ito para kay <xliff:g id="USERNAME">%1$s</xliff:g>? Ise-save ang iyong personal na data"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Gusto mo bang i-archive ang app na ito mula sa iyong pribadong space? Ise-save ang iyong personal na data"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Gusto mo bang i-uninstall ang app na ito para sa "<b>"lahat"</b>" ng user? Aalisin ang application at ang data nito sa "<b>"lahat"</b>" ng user sa device."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Gusto mo bang i-uninstall ang app na ito para sa user na si <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Gusto mo bang i-uninstall ang app na ito sa iyong profile sa trabaho?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Mas nanganganib ang iyong tablet at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon kang ikaw ang responsable sa anumang pinsala sa iyong tablet o pagkawala ng data na maaaring magresulta mula sa paggamit nito."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Mas nanganganib ang iyong TV at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon kang ikaw ang responsable sa anumang pinsala sa iyong TV o pagkawala ng data na maaaring magresulta mula sa paggamit nito."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Clone ng <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"I-archive ang <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Magpatuloy"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Mga Setting"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Ini-install/ina-uninstall ang wear app"</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 3a0e053..79d7a94 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Bilinmeyen uygulamalar bu kullanıcı tarafından yüklenemez"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Bu kullanıcının uygulama yüklemesine izin verilmiyor"</string>
<string name="ok" msgid="7871959885003339302">"Tamam"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arşiv"</string>
<string name="update_anyway" msgid="8792432341346261969">"Yine de güncelle"</string>
<string name="manage_applications" msgid="5400164782453975580">"Uygulamaları yönet"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Yer kalmadı"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Güncelleme kaldırılsın mı?"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, şu uygulamanın bir parçasıdır:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Kişisel verileriniz kaydedilir"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Bu uygulama tüm kullanıcılar için arşivlensin mi? Kişisel verileriniz kaydedilir"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Bu uygulama iş profilinizde arşivlensin mi? Kişisel verileriniz kaydedilir"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Bu uygulama <xliff:g id="USERNAME">%1$s</xliff:g> için arşivlensin mi? Kişisel verileriniz kaydedilir"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Özel alanınızdaki bu uygulamayı arşivlemek istiyor musunuz? Kişisel verileriniz kaydedilir"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Bu uygulamanın yüklemesini "<b>"tüm"</b>" kullanıcılar için kaldırmak istiyor musunuz? Uygulama ve verileri cihazdan "<b>"tüm"</b>" kullanıcılar için kaldırılacaktır."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"<xliff:g id="USERNAME">%1$s</xliff:g> adlı kullanıcı için bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Bu uygulamanın iş profilinizdeki yüklemesini kaldırmak istiyor musunuz?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletiniz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı tabletinizde oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV\'niz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı TV\'nizde oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> Klonu"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> arşivlensin mi?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Devam"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ayarlar"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear uygulamalarını yükleme/kaldırma"</string>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
index 4ac9185..fe966f7 100644
--- a/packages/PackageInstaller/res/values-uk/strings.xml
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Цей користувач не може встановлювати невідомі додатки"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Цей користувач не може встановлювати додатки"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Архівувати"</string>
<string name="update_anyway" msgid="8792432341346261969">"Усе одно оновити"</string>
<string name="manage_applications" msgid="5400164782453975580">"Керувати додатками"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Недостат. місця"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Видалити оновлення"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"Дія <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> є частиною такої програми:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Видалити цей додаток?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Ваші особисті дані буде збережено"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Архівувати цей додаток для всіх користувачів? Ваші особисті дані буде збережено"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Архівувати цей додаток у вашому робочому профілі? Ваші особисті дані буде збережено"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Архівувати цей додаток для користувача <xliff:g id="USERNAME">%1$s</xliff:g>? Ваші особисті дані буде збережено"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Архівувати цей додаток із вашого приватного простору? Ваші особисті дані буде збережено"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Хочете видалити цю програму для "<b>"всіх"</b>" користувачів? Програму та її дані буде видалено для "<b>"всіх"</b>" користувачів цього пристрою."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Видалити цей додаток для користувача <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Видалити цей додаток із вашого робочого профілю?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ваш планшет і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження планшета чи втрату даних унаслідок використання додатка."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Ваш телевізор і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження телевізора чи втрату даних унаслідок використання додатка."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Копія додатка <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Архівувати додаток <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Продовжити"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Налаштування"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Встановлення або видалення додатків Wear"</string>
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
index 6730574..98d6e8f 100644
--- a/packages/PackageInstaller/res/values-ur/strings.xml
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"یہ صارف نامعلوم ایپس کو انسٹال نہیں کر سکتا"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"اس صارف کو ایپس انسٹال کرنے کی اجازت نہیں ہے"</string>
<string name="ok" msgid="7871959885003339302">"ٹھیک ہے"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"آرکائیو کریں"</string>
<string name="update_anyway" msgid="8792432341346261969">"بہر حال اپ ڈیٹ کریں"</string>
<string name="manage_applications" msgid="5400164782453975580">"ایپس منظم کریں"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"جگہ نہیں ہے"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"اپ ڈیٹ اَن انسٹال کریں"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> درج ذیل ایپ کا حصہ ہے:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"کیا آپ اس ایپ کو اَن انسٹال کرنا چاہتے ہیں؟"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"آپ کا ذاتی ڈیٹا محفوظ ہو جائے گا"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"اس ایپ کو تمام صارفین کے لیے آرکائیو کریں؟ آپ کا ذاتی ڈیٹا محفوظ ہو جائے گا"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"اس ایپ کو اپنی دفتری پروفائل پر آرکائیو کریں؟ آپ کا ذاتی ڈیٹا محفوظ ہو جائے گا"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"<xliff:g id="USERNAME">%1$s</xliff:g> کے لیے اس ایپ کو آرکائیو کریں؟ آپ کا ذاتی ڈیٹا محفوظ ہو جائے گا"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"کیا آپ اس ایپ کو اپنی نجی جگہ سے آرکائیو کرنا چاہتے ہیں؟ آپ کا ذاتی ڈیٹا محفوظ ہو جائے گا"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"کیا آپ "<b>"سبھی"</b>" صارفین کیلئے اس ایپ کو اَن انسٹال کرنا چاہتے ہیں؟ ایپلیکیشن اور اس کا ڈیٹا آلہ پر موجود "<b>"سبھی"</b>" صارفین سے ہٹا دیا جائے گا۔"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"کیا آپ اس ایپ کو صارف <xliff:g id="USERNAME">%1$s</xliff:g> کیلئے اَن انسٹال کرنا چاہتے ہیں؟"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"کیا آپ اپنے دفتری پروفائل سے یہ ایپ اَن انسٹال کرنا چاہتے ہیں؟"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"آپ کے ٹیبلیٹ اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے ٹیبلیٹ کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کے لئے خود ذمہ دار ہیں۔"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"آپ کے TV اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے TV کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کے لئے خود ذمہ دار ہیں۔"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کلون"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کو آرکائیو کریں؟"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"جاری رکھیں"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"ترتیبات"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"wear ایپس کو انسٹال/اَن انسٹال کرنا"</string>
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
index cc5f6bdd..843efe9 100644
--- a/packages/PackageInstaller/res/values-uz/strings.xml
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Notanish ilovalarni bu foydalanuvchi tomonidan o‘rnatib bo‘lmaydi"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Bu foydalanuvchiga ilovalarni o‘rnatish uchun ruxsat berilmagan"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Arxivlash"</string>
<string name="update_anyway" msgid="8792432341346261969">"Baribir yangilansin"</string>
<string name="manage_applications" msgid="5400164782453975580">"Ilovalarni boshqarish"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Joy qolmadi"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Yangilanishni o‘chirish"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> quyidagi ilovaning bir qismidir:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Bu ilovani o‘chirib tashlamoqchimisiz?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Shaxsiy maʼlumotlar saqlanadi"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Bu ilova barcha foydalanuvchilar uchun arxivlansinmi? Shaxsiy maʼlumotlar saqlanadi"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Ish profilidagi mazkur ilova arxivlansinmi? Shaxsiy maʼlumotlar saqlanadi"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Bu ilova <xliff:g id="USERNAME">%1$s</xliff:g> uchun arxivlansinmi? Shaxsiy maʼlumotlar saqlanadi"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Maxfiy joydagi mazkur ilova arxivlansinmi? Shaxsiy maʼlumotlar saqlanadi"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ushbu ilova "<b>"barcha"</b>" foydalanuvchilar uchun o‘chirilsinmi? Ilova va uning axborotlari qurilmadagi "<b>"barcha"</b>" foydalanuvchilardan o‘chib ketadi."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Haqiqatdan ham <xliff:g id="USERNAME">%1$s</xliff:g> foydalanuvchi uchun ushbu ilovani olib tashlamoqchimisiz?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Bu ilova ish profilidan olib tashlansinmi?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Planshetingiz va shaxsiy axborotlaringiz notanish ilovalar hujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan planshetingizga yetkaziladigan shikast va axborotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV va shaxsiy axborotlaringiz notanish ilovalar hujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan televizoringizga yetkaziladigan shikast va axborotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nusxasi"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> arxivlansinmi?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Davom etish"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Sozlamalar"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear ilovalarini o‘rnatish/o‘chirish"</string>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
index 6af9b82..40e6dd5 100644
--- a/packages/PackageInstaller/res/values-vi/strings.xml
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Người dùng này không thể cài đặt ứng dụng không xác định"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Người dùng này không được phép cài đặt ứng dụng"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Lưu trữ"</string>
<string name="update_anyway" msgid="8792432341346261969">"Vẫn cập nhật"</string>
<string name="manage_applications" msgid="5400164782453975580">"Quản lý ứng dụng"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Hết dung lượng"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Gỡ cài đặt bản cập nhật"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> là một phần của ứng dụng sau:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Bạn có muốn gỡ cài đặt ứng dụng này không?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Dữ liệu cá nhân của bạn sẽ được lưu"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Lưu trữ ứng dụng này cho tất cả người dùng. Dữ liệu cá nhân của bạn sẽ được lưu"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Lưu trữ ứng dụng này trên hồ sơ công việc của bạn? Dữ liệu cá nhân của bạn sẽ được lưu"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Lưu trữ ứng dụng này cho <xliff:g id="USERNAME">%1$s</xliff:g>? Dữ liệu cá nhân của bạn sẽ được lưu"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Bạn có muốn lưu trữ ứng dụng này ra khỏi không gian riêng tư của mình không? Dữ liệu cá nhân của bạn sẽ được lưu"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Bạn có muốn gỡ cài đặt ứng dụng này cho "<b>"tất cả"</b>" người dùng không? Ứng dụng và dữ liệu của ứng dụng sẽ bị xóa khỏi "<b>"tất cả"</b>" người dùng trên thiết bị."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Bạn có muốn gỡ cài đặt ứng dụng này cho người dùng <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Bạn có muốn gỡ cài đặt ứng dụng này khỏi hồ sơ công việc của mình không?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Máy tính bảng và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với máy tính bảng của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"TV và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với TV của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"Bản sao của <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Lưu trữ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Tiếp tục"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Cài đặt"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Cài đặt/gỡ cài đặt ứng dụng Wear"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
index 3e77d7f..605726a 100644
--- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"该用户无法安装未知应用"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"此用户无权安装应用"</string>
<string name="ok" msgid="7871959885003339302">"确定"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"归档"</string>
<string name="update_anyway" msgid="8792432341346261969">"仍然更新"</string>
<string name="manage_applications" msgid="5400164782453975580">"管理应用"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"空间不足"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"卸载更新"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>属于以下应用:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"要卸载此应用吗?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"系统将保存您的个人数据"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"要针对所有用户归档此应用吗?系统将保存您的个人数据"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"要归档工作资料中的此应用吗?系统将保存您的个人数据"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"要针对<xliff:g id="USERNAME">%1$s</xliff:g>归档此应用吗?系统将保存您的个人数据"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"要归档私密空间中的此应用吗?系统将保存您的个人数据"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"要为"<b>"所有"</b>"用户卸载此应用吗?系统将为设备上的"<b>"所有"</b>"用户移除此应用及其数据。"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"要为用户<xliff:g id="USERNAME">%1$s</xliff:g>卸载此应用吗?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"您想从您的工作资料中卸载此应用吗?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"来历不明的应用很可能会损害您的平板电脑和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何平板电脑损坏或数据丢失情况,您负有全部责任。"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"来历不明的应用很可能会损害您的电视和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何电视损坏或数据丢失情况,您负有全部责任。"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 克隆应用"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"要归档<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>吗?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"继续"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"设置"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"正在安装/卸载 Wear 应用"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
index 4d2edea..ab7b7ff 100644
--- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"此使用者無法安裝來源不明的應用程式"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"此使用者無法安裝應用程式"</string>
<string name="ok" msgid="7871959885003339302">"確定"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"封存"</string>
<string name="update_anyway" msgid="8792432341346261969">"仍要更新"</string>
<string name="manage_applications" msgid="5400164782453975580">"管理應用程式"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"儲存空間不足"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"解除安裝更新"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"「<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>」屬於以下應用程式:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"你要解除安裝此應用程式嗎?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"要為所有使用者封存此應用程式嗎?系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"要封存工作設定檔中的這個應用程式嗎?系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"要為<xliff:g id="USERNAME">%1$s</xliff:g>封存此應用程式嗎?系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"要封存私人空間中的這個應用程式嗎?系統會儲存你的個人資料"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"你要為"<b>"所有"</b>"使用者解除安裝這個應用程式嗎?應用程式及其資料會從裝置上的"<b>"所有"</b>"使用者設定檔中移除。"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"你要為使用者<xliff:g id="USERNAME">%1$s</xliff:g>解除安裝此應用程式嗎?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"要從工作設定檔解除安裝此應用程式嗎?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"來源不明的應用程式可能會侵害你的平板電腦和個人資料。安裝此應用程式,即表示你同意承擔因使用這個應用程式而導致平板電腦損壞或資料遺失的責任。"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"來源不明的應用程式可能會侵害你的電視和個人資料。安裝此應用程式,即表示你同意承擔因使用這個應用程式而導致電視損壞或資料遺失的責任。"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」複製本"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"要封存<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>嗎?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"繼續"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"設定"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"正在安裝/解除安裝 Wear 應用程式"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
index 30e66a0..16d9a30 100644
--- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"這位使用者無法安裝不明的應用程式"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"這位使用者無法安裝應用程式"</string>
<string name="ok" msgid="7871959885003339302">"確定"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"封存"</string>
<string name="update_anyway" msgid="8792432341346261969">"仍要更新"</string>
<string name="manage_applications" msgid="5400164782453975580">"管理應用程式"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"空間不足"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"解除安裝更新"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"「<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>」屬於下列應用程式:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"要解除安裝這個應用程式嗎?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"要為所有使用者封存這個應用程式嗎?系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"要在工作資料夾封存這個應用程式嗎?系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"要為<xliff:g id="USERNAME">%1$s</xliff:g>封存這個應用程式嗎?系統會儲存你的個人資料"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"要從私人空間封存這個應用程式嗎?系統會儲存你的個人資料"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"要為"<b>"所有"</b>"使用者解除安裝這個應用程式嗎?該應用程式及其資料會從裝置上的"<b>"所有"</b>"使用者設定檔移除。"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"要為使用者 <xliff:g id="USERNAME">%1$s</xliff:g> 解除安裝這個應用程式嗎?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"要從工作資料夾解除安裝這個應用程式嗎?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"來歷不明的應用程式可能會損害你的平板電腦和個人資料。如因安裝及使用這個應用程式,導致你的平板電腦受損或資料遺失,請自行負責。"</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"來歷不明的應用程式可能會損害你的電視和個人資料。如因安裝及使用這個應用程式,導致你的電視受損或資料遺失,請自行負責。"</string>
<string name="cloned_app_label" msgid="7503612829833756160">"「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」副本"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"要封存「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」嗎?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"繼續"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"設定"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"安裝/解除安裝中的 Wear 應用程式"</string>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index 0bfc9b7..624ed0f 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -44,8 +44,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Izinhlelo zokusebenza ezingaziwa azikwazi ukufakwa ilo msebenzisi"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Lo msebenzisi akavunyelwe ukufaka izinhlelo zokusebenza"</string>
<string name="ok" msgid="7871959885003339302">"KULUNGILE"</string>
- <!-- no translation found for archive (4447791830199354721) -->
- <skip />
+ <string name="archive" msgid="4447791830199354721">"Ingobo yomlando"</string>
<string name="update_anyway" msgid="8792432341346261969">"Buyekeza noma kunjalo"</string>
<string name="manage_applications" msgid="5400164782453975580">"Phatha izinhlelo zokusebenza"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Iphelelwe yisikhala"</string>
@@ -60,16 +59,11 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Khipha isibuyekezo"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"I-<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> ingxenye yohlelo lokusebenza olulandelayo:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Ufuna ukukhipha le app?"</string>
- <!-- no translation found for archive_application_text (8482325710714386348) -->
- <skip />
- <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
- <skip />
- <!-- no translation found for archive_application_text_user (2586558895535581451) -->
- <skip />
- <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
- <skip />
+ <string name="archive_application_text" msgid="8482325710714386348">"Idatha yakho yomuntu siqu izolondolozwa"</string>
+ <string name="archive_application_text_all_users" msgid="3151229641681672580">"Faka le app kungobo yomlando yabo bonke abasebenzisi? Idatha yakho yomuntu siqu izolondolozwa"</string>
+ <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Faka kungobo yomlando le app ekuphrofayela yakho yomsebenzi? Idatha yakho yomuntu siqu izolondolozwa"</string>
+ <string name="archive_application_text_user" msgid="2586558895535581451">"Faka le app kungobo yamlando uyifakele u-<xliff:g id="USERNAME">%1$s</xliff:g>? Idatha yakho yomuntu siqu izolondolozwa"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Ingabe ufuna ukuthi le app esendaweni yakho yangasese ifakwe kungobo yomlando? Idatha yakho yomuntu siqu izolondolozwa"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"Ingabe ufuna ukukhipha lolu hlelo lokusebenza kubo "<b>"bonke"</b>" abasebenzisi? Uhlelo lokusebenza nedatha yalo kuzosuswa kubo "<b>"bonke"</b>" abasebenzisi kudivayisi."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Ingabe ufuna ukukhiphela lolu hlelo lokusebenza kumsebenzisi ongu-<xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Ingabe ufuna ukukhipha le app kusukela kuphrofayela yakho yokusebenza?"</string>
@@ -108,8 +102,7 @@
<string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Ithebulethi yakho nedatha yomuntu siqu zisengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala kuthebulethi yakho noma ukulahleka kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
<string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Idatha yakho ye-TV neyomuntu siqu isengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala ku-TV yakho noma ukulahlekelwa kwedatha okungabangelwa ukusetshenziswa kwayo."</string>
<string name="cloned_app_label" msgid="7503612829833756160">"I-Clone ye-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>"</string>
- <!-- no translation found for archiving_app_label (1127085259724124725) -->
- <skip />
+ <string name="archiving_app_label" msgid="1127085259724124725">"Faka i-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kungobo yomlando?"</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Qhubeka"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Amasethingi"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Ifaka/ikhipha izinhlelo zokusebenza ze-wear"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java
deleted file mode 100644
index c8175ad..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.java
+++ /dev/null
@@ -1,912 +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.packageinstaller.v2.model;
-
-import static com.android.packageinstaller.v2.model.PackageUtil.canPackageQuery;
-import static com.android.packageinstaller.v2.model.PackageUtil.generateStubPackageInfo;
-import static com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet;
-import static com.android.packageinstaller.v2.model.PackageUtil.getPackageInfo;
-import static com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid;
-import static com.android.packageinstaller.v2.model.PackageUtil.isCallerSessionOwner;
-import static com.android.packageinstaller.v2.model.PackageUtil.isInstallPermissionGrantedOrRequested;
-import static com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_DONE;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_INTERNAL_ERROR;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.ABORT_REASON_POLICY;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallAborted.DLG_PACKAGE_ERROR;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION;
-import static com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.InstallSourceInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.common.EventResultPersister;
-import com.android.packageinstaller.common.InstallEventReceiver;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-import com.android.packageinstaller.v2.model.installstagedata.InstallAborted;
-import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
-import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
-import com.android.packageinstaller.v2.model.installstagedata.InstallReady;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStaging;
-import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
-import java.io.File;
-import java.io.IOException;
-
-public class InstallRepository {
-
- public static final String EXTRA_STAGED_SESSION_ID =
- "com.android.packageinstaller.extra.STAGED_SESSION_ID";
- private static final String SCHEME_PACKAGE = "package";
- private static final String BROADCAST_ACTION =
- "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
- private static final String TAG = InstallRepository.class.getSimpleName();
- private final Context mContext;
- private final PackageManager mPackageManager;
- private final PackageInstaller mPackageInstaller;
- private final UserManager mUserManager;
- private final DevicePolicyManager mDevicePolicyManager;
- private final AppOpsManager mAppOpsManager;
- private final MutableLiveData<InstallStage> mStagingResult = new MutableLiveData<>();
- private final MutableLiveData<InstallStage> mInstallResult = new MutableLiveData<>();
- private final boolean mLocalLOGV = false;
- private Intent mIntent;
- private boolean mIsSessionInstall;
- private boolean mIsTrustedSource;
- /**
- * Session ID for a session created when caller uses PackageInstaller APIs
- */
- private int mSessionId;
- /**
- * Session ID for a session created by this app
- */
- private int mStagedSessionId = SessionInfo.INVALID_ID;
- private int mCallingUid;
- private String mCallingPackage;
- private SessionStager mSessionStager;
- private AppOpRequestInfo mAppOpRequestInfo;
- private AppSnippet mAppSnippet;
- /**
- * PackageInfo of the app being installed on device.
- */
- private PackageInfo mNewPackageInfo;
-
- public InstallRepository(Context context) {
- mContext = context;
- mPackageManager = context.getPackageManager();
- mPackageInstaller = mPackageManager.getPackageInstaller();
- mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
- mUserManager = context.getSystemService(UserManager.class);
- mAppOpsManager = context.getSystemService(AppOpsManager.class);
- }
-
- /**
- * Extracts information from the incoming install intent, checks caller's permission to install
- * packages, verifies that the caller is the install session owner (in case of a session based
- * install) and checks if the current user has restrictions set that prevent app installation,
- *
- * @param intent the incoming {@link Intent} object for installing a package
- * @param callerInfo {@link CallerInfo} that holds the callingUid and callingPackageName
- * @return <p>{@link InstallAborted} if there are errors while performing the checks</p>
- * <p>{@link InstallStaging} after successfully performing the checks</p>
- */
- public InstallStage performPreInstallChecks(Intent intent, CallerInfo callerInfo) {
- mIntent = intent;
-
- String callingAttributionTag = null;
-
- mIsSessionInstall =
- PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction())
- || PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
-
- mSessionId = mIsSessionInstall
- ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, SessionInfo.INVALID_ID)
- : SessionInfo.INVALID_ID;
-
- mStagedSessionId = mIntent.getIntExtra(EXTRA_STAGED_SESSION_ID, SessionInfo.INVALID_ID);
-
- mCallingPackage = callerInfo.getPackageName();
-
- if (mCallingPackage == null && mSessionId != SessionInfo.INVALID_ID) {
- PackageInstaller.SessionInfo sessionInfo = mPackageInstaller.getSessionInfo(mSessionId);
- mCallingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
- callingAttributionTag =
- (sessionInfo != null) ? sessionInfo.getInstallerAttributionTag() : null;
- }
-
- // Uid of the source package, coming from ActivityManager
- mCallingUid = callerInfo.getUid();
- if (mCallingUid == Process.INVALID_UID) {
- Log.e(TAG, "Could not determine the launching uid.");
- }
- final ApplicationInfo sourceInfo = getSourceInfo(mCallingPackage);
- // Uid of the source package, with a preference to uid from ApplicationInfo
- final int originatingUid = sourceInfo != null ? sourceInfo.uid : mCallingUid;
- mAppOpRequestInfo = new AppOpRequestInfo(
- getPackageNameForUid(mContext, originatingUid, mCallingPackage),
- originatingUid, callingAttributionTag);
-
- if (mCallingUid == Process.INVALID_UID && sourceInfo == null) {
- // Caller's identity could not be determined. Abort the install
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
-
- if ((mSessionId != SessionInfo.INVALID_ID
- && !isCallerSessionOwner(mPackageInstaller, originatingUid, mSessionId))
- || (mStagedSessionId != SessionInfo.INVALID_ID
- && !isCallerSessionOwner(mPackageInstaller, Process.myUid(), mStagedSessionId))) {
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
-
- mIsTrustedSource = isInstallRequestFromTrustedSource(sourceInfo, mIntent, originatingUid);
-
- if (!isInstallPermissionGrantedOrRequested(mContext, mCallingUid, originatingUid,
- mIsTrustedSource)) {
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
-
- String restriction = getDevicePolicyRestrictions();
- if (restriction != null) {
- InstallAborted.Builder abortedBuilder =
- new InstallAborted.Builder(ABORT_REASON_POLICY).setMessage(restriction);
- final Intent adminSupportDetailsIntent =
- mDevicePolicyManager.createAdminSupportIntent(restriction);
- if (adminSupportDetailsIntent != null) {
- abortedBuilder.setResultIntent(adminSupportDetailsIntent);
- }
- return abortedBuilder.build();
- }
-
- maybeRemoveInvalidInstallerPackageName(callerInfo);
-
- return new InstallStaging();
- }
-
- /**
- * @return the ApplicationInfo for the installation source (the calling package), if available
- */
- @Nullable
- private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
- if (callingPackage == null) {
- return null;
- }
- try {
- return mPackageManager.getApplicationInfo(callingPackage, 0);
- } catch (PackageManager.NameNotFoundException ignored) {
- return null;
- }
- }
-
- private boolean isInstallRequestFromTrustedSource(ApplicationInfo sourceInfo, Intent intent,
- int originatingUid) {
- boolean isNotUnknownSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
- return sourceInfo != null && sourceInfo.isPrivilegedApp()
- && (isNotUnknownSource
- || isPermissionGranted(mContext, Manifest.permission.INSTALL_PACKAGES, originatingUid));
- }
-
- private String getDevicePolicyRestrictions() {
- final String[] restrictions = new String[]{
- UserManager.DISALLOW_INSTALL_APPS,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
- };
-
- for (String restriction : restrictions) {
- if (!mUserManager.hasUserRestrictionForUser(restriction, Process.myUserHandle())) {
- continue;
- }
- return restriction;
- }
- return null;
- }
-
- private void maybeRemoveInvalidInstallerPackageName(CallerInfo callerInfo) {
- final String installerPackageNameFromIntent =
- mIntent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
- if (installerPackageNameFromIntent == null) {
- return;
- }
- if (!TextUtils.equals(installerPackageNameFromIntent, callerInfo.getPackageName())
- && !isPermissionGranted(mPackageManager, Manifest.permission.INSTALL_PACKAGES,
- callerInfo.getPackageName())) {
- Log.e(TAG, "The given installer package name " + installerPackageNameFromIntent
- + " is invalid. Remove it.");
- EventLog.writeEvent(0x534e4554, "236687884", callerInfo.getUid(),
- "Invalid EXTRA_INSTALLER_PACKAGE_NAME");
- mIntent.removeExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
- }
- }
-
- public void stageForInstall() {
- Uri uri = mIntent.getData();
- if (mStagedSessionId != SessionInfo.INVALID_ID
- || mIsSessionInstall
- || (uri != null && SCHEME_PACKAGE.equals(uri.getScheme()))) {
- // For a session based install or installing with a package:// URI, there is no file
- // for us to stage.
- mStagingResult.setValue(new InstallReady());
- return;
- }
- if (uri != null
- && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
- && canPackageQuery(mContext, mCallingUid, uri)) {
-
- if (mStagedSessionId > 0) {
- final PackageInstaller.SessionInfo info =
- mPackageInstaller.getSessionInfo(mStagedSessionId);
- if (info == null || !info.isActive() || info.getResolvedBaseApkPath() == null) {
- Log.w(TAG, "Session " + mStagedSessionId + " in funky state; ignoring");
- if (info != null) {
- cleanupStagingSession();
- }
- mStagedSessionId = 0;
- }
- }
-
- // Session does not exist, or became invalid.
- if (mStagedSessionId <= 0) {
- // Create session here to be able to show error.
- try (final AssetFileDescriptor afd =
- mContext.getContentResolver().openAssetFileDescriptor(uri, "r")) {
- ParcelFileDescriptor pfd = afd != null ? afd.getParcelFileDescriptor() : null;
- PackageInstaller.SessionParams params =
- createSessionParams(mIntent, pfd, uri.toString());
- mStagedSessionId = mPackageInstaller.createSession(params);
- } catch (IOException e) {
- Log.w(TAG, "Failed to create a staging session", e);
- mStagingResult.setValue(
- new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
- .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
- PackageManager.INSTALL_FAILED_INVALID_APK))
- .setActivityResultCode(Activity.RESULT_FIRST_USER)
- .build());
- return;
- }
- }
-
- SessionStageListener listener = new SessionStageListener() {
- @Override
- public void onStagingSuccess(SessionInfo info) {
- //TODO: Verify if the returned sessionInfo should be used anywhere
- mStagingResult.setValue(new InstallReady());
- }
-
- @Override
- public void onStagingFailure() {
- cleanupStagingSession();
- mStagingResult.setValue(
- new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
- .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
- PackageManager.INSTALL_FAILED_INVALID_APK))
- .setActivityResultCode(Activity.RESULT_FIRST_USER)
- .build());
- }
- };
- if (mSessionStager != null) {
- mSessionStager.cancel(true);
- }
- mSessionStager = new SessionStager(mContext, uri, mStagedSessionId, listener);
- mSessionStager.execute();
- }
- }
-
- public int getStagedSessionId() {
- return mStagedSessionId;
- }
-
- private void cleanupStagingSession() {
- if (mStagedSessionId > 0) {
- try {
- mPackageInstaller.abandonSession(mStagedSessionId);
- } catch (SecurityException ignored) {
- }
- mStagedSessionId = 0;
- }
- }
-
- private PackageInstaller.SessionParams createSessionParams(@NonNull Intent intent,
- @Nullable ParcelFileDescriptor pfd, @NonNull String debugPathName) {
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- final Uri referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER, Uri.class);
- params.setPackageSource(
- referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
- : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
- params.setInstallAsInstantApp(false);
- params.setReferrerUri(referrerUri);
- params.setOriginatingUri(
- intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI, Uri.class));
- params.setOriginatingUid(intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
- Process.INVALID_UID));
- params.setInstallerPackageName(intent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME));
- params.setInstallReason(PackageManager.INSTALL_REASON_USER);
- // Disable full screen intent usage by for sideloads.
- params.setPermissionState(Manifest.permission.USE_FULL_SCREEN_INTENT,
- PackageInstaller.SessionParams.PERMISSION_STATE_DENIED);
-
- if (pfd != null) {
- try {
- final PackageInstaller.InstallInfo result = mPackageInstaller.readInstallInfo(pfd,
- debugPathName, 0);
- params.setAppPackageName(result.getPackageName());
- params.setInstallLocation(result.getInstallLocation());
- params.setSize(result.calculateInstalledSize(params, pfd));
- } catch (PackageInstaller.PackageParsingException e) {
- Log.e(TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.", e);
- params.setSize(pfd.getStatSize());
- } catch (IOException e) {
- Log.e(TAG,
- "Cannot calculate installed size " + debugPathName
- + ". Try only apk size.", e);
- }
- } else {
- Log.e(TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.");
- }
- return params;
- }
-
- /**
- * Processes Install session, file:// or package:// URI to generate data pertaining to user
- * confirmation for an install. This method also checks if the source app has the AppOp granted
- * to install unknown apps. If an AppOp is to be requested, cache the user action prompt data to
- * be reused once appOp has been granted
- *
- * @return <ul>
- * <li>InstallAborted </li>
- * <ul>
- * <li> If install session is invalid (not sealed or resolvedBaseApk path
- * is invalid) </li>
- * <li> Source app doesn't have visibility to target app </li>
- * <li> The APK is invalid </li>
- * <li> URI is invalid </li>
- * <li> Can't get ApplicationInfo for source app, to request AppOp </li>
- * </ul>
- * <li> InstallUserActionRequired</li>
- * <ul>
- * <li> If AppOP is granted and user action is required to proceed
- * with install </li>
- * <li> If AppOp grant is to be requested from the user</li>
- * </ul>
- * </ul>
- */
- public InstallStage requestUserConfirmation() {
- if (mIsTrustedSource) {
- if (mLocalLOGV) {
- Log.i(TAG, "install allowed");
- }
- // Returns InstallUserActionRequired stage if install details could be successfully
- // computed, else it returns InstallAborted.
- return generateConfirmationSnippet();
- } else {
- InstallStage unknownSourceStage = handleUnknownSources(mAppOpRequestInfo);
- if (unknownSourceStage.getStageCode() == InstallStage.STAGE_READY) {
- // Source app already has appOp granted.
- return generateConfirmationSnippet();
- } else {
- return unknownSourceStage;
- }
- }
- }
-
-
- private InstallStage generateConfirmationSnippet() {
- final Object packageSource;
- int pendingUserActionReason = -1;
- if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(mIntent.getAction())) {
- final SessionInfo info = mPackageInstaller.getSessionInfo(mSessionId);
- String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null;
-
- if (info == null || !info.isSealed() || resolvedPath == null) {
- Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
- packageSource = Uri.fromFile(new File(resolvedPath));
- // TODO: Not sure where is this used yet. PIA.java passes it to
- // InstallInstalling if not null
- // mOriginatingURI = null;
- // mReferrerURI = null;
- pendingUserActionReason = info.getPendingUserActionReason();
- } else if (PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(mIntent.getAction())) {
- final SessionInfo info = mPackageInstaller.getSessionInfo(mSessionId);
-
- if (info == null || !info.isPreApprovalRequested()) {
- Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
- packageSource = info;
- // mOriginatingURI = null;
- // mReferrerURI = null;
- pendingUserActionReason = info.getPendingUserActionReason();
- } else {
- // Two possible origins:
- // 1. Installation with SCHEME_PACKAGE.
- // 2. Installation with "file://" for session created by this app
- if (mIntent.getData() != null && mIntent.getData().getScheme().equals(SCHEME_PACKAGE)) {
- packageSource = mIntent.getData();
- } else {
- SessionInfo stagedSessionInfo = mPackageInstaller.getSessionInfo(mStagedSessionId);
- packageSource = Uri.fromFile(new File(stagedSessionInfo.getResolvedBaseApkPath()));
- }
- // mOriginatingURI = mIntent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
- // mReferrerURI = mIntent.getParcelableExtra(Intent.EXTRA_REFERRER);
- pendingUserActionReason = PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE;
- }
-
- // if there's nothing to do, quietly slip into the ether
- if (packageSource == null) {
- Log.w(TAG, "Unspecified source");
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
- .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
- PackageManager.INSTALL_FAILED_INVALID_URI))
- .setActivityResultCode(Activity.RESULT_FIRST_USER)
- .build();
- }
-
- return processAppSnippet(packageSource, pendingUserActionReason);
- }
-
- /**
- * Parse the Uri (post-commit install session) or use the SessionInfo (pre-commit install
- * session) to set up the installer for this install.
- *
- * @param source The source of package URI or SessionInfo
- * @return {@code true} iff the installer could be set up
- */
- private InstallStage processAppSnippet(Object source, int userActionReason) {
- if (source instanceof Uri) {
- return processPackageUri((Uri) source, userActionReason);
- } else if (source instanceof SessionInfo) {
- return processSessionInfo((SessionInfo) source, userActionReason);
- }
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
-
- /**
- * Parse the Uri and set up the installer for this package.
- *
- * @param packageUri The URI to parse
- * @return {@code true} iff the installer could be set up
- */
- private InstallStage processPackageUri(final Uri packageUri, int userActionReason) {
- final String scheme = packageUri.getScheme();
- final String packageName = packageUri.getSchemeSpecificPart();
-
- if (scheme == null) {
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
-
- if (mLocalLOGV) {
- Log.i(TAG, "processPackageUri(): uri = " + packageUri + ", scheme = " + scheme);
- }
-
- switch (scheme) {
- case SCHEME_PACKAGE -> {
- for (UserHandle handle : mUserManager.getUserHandles(true)) {
- PackageManager pmForUser = mContext.createContextAsUser(handle, 0)
- .getPackageManager();
- try {
- if (pmForUser.canPackageQuery(mCallingPackage, packageName)) {
- mNewPackageInfo = pmForUser.getPackageInfo(packageName,
- PackageManager.GET_PERMISSIONS
- | PackageManager.MATCH_UNINSTALLED_PACKAGES);
- }
- } catch (NameNotFoundException ignored) {
- }
- }
- if (mNewPackageInfo == null) {
- Log.w(TAG, "Requested package " + packageUri.getSchemeSpecificPart()
- + " not available. Discontinuing installation");
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
- .setErrorDialogType(DLG_PACKAGE_ERROR)
- .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
- PackageManager.INSTALL_FAILED_INVALID_APK))
- .setActivityResultCode(Activity.RESULT_FIRST_USER)
- .build();
- }
- mAppSnippet = getAppSnippet(mContext, mNewPackageInfo);
- if (mLocalLOGV) {
- Log.i(TAG, "Created snippet for " + mAppSnippet.getLabel());
- }
- }
- case ContentResolver.SCHEME_FILE -> {
- File sourceFile = new File(packageUri.getPath());
- mNewPackageInfo = getPackageInfo(mContext, sourceFile,
- PackageManager.GET_PERMISSIONS);
-
- // Check for parse errors
- if (mNewPackageInfo == null) {
- Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
- .setErrorDialogType(DLG_PACKAGE_ERROR)
- .setResultIntent(new Intent().putExtra(Intent.EXTRA_INSTALL_RESULT,
- PackageManager.INSTALL_FAILED_INVALID_APK))
- .setActivityResultCode(Activity.RESULT_FIRST_USER)
- .build();
- }
- if (mLocalLOGV) {
- Log.i(TAG, "Creating snippet for local file " + sourceFile);
- }
- mAppSnippet = getAppSnippet(mContext, mNewPackageInfo.applicationInfo, sourceFile);
- }
- default -> {
- Log.e(TAG, "Unexpected URI scheme " + packageUri);
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
- }
-
- return new InstallUserActionRequired.Builder(
- USER_ACTION_REASON_INSTALL_CONFIRMATION, mAppSnippet)
- .setDialogMessage(getUpdateMessage(mNewPackageInfo, userActionReason))
- .setAppUpdating(isAppUpdating(mNewPackageInfo))
- .build();
- }
-
- /**
- * Use the SessionInfo and set up the installer for pre-commit install session.
- *
- * @param sessionInfo The SessionInfo to compose
- * @return {@code true} iff the installer could be set up
- */
- private InstallStage processSessionInfo(@NonNull SessionInfo sessionInfo,
- int userActionReason) {
- mNewPackageInfo = generateStubPackageInfo(sessionInfo.getAppPackageName());
-
- mAppSnippet = getAppSnippet(mContext, sessionInfo);
- return new InstallUserActionRequired.Builder(
- USER_ACTION_REASON_INSTALL_CONFIRMATION, mAppSnippet)
- .setAppUpdating(isAppUpdating(mNewPackageInfo))
- .setDialogMessage(getUpdateMessage(mNewPackageInfo, userActionReason))
- .build();
- }
-
- private String getUpdateMessage(PackageInfo pkgInfo, int userActionReason) {
- if (isAppUpdating(pkgInfo)) {
- final CharSequence existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo);
- final CharSequence requestedUpdateOwnerLabel = getApplicationLabel(mCallingPackage);
-
- if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
- && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP) {
- return mContext.getString(R.string.install_confirm_question_update_owner_reminder,
- requestedUpdateOwnerLabel, existingUpdateOwnerLabel);
- }
- }
- return null;
- }
-
- private CharSequence getExistingUpdateOwnerLabel(PackageInfo pkgInfo) {
- try {
- final String packageName = pkgInfo.packageName;
- final InstallSourceInfo sourceInfo = mPackageManager.getInstallSourceInfo(packageName);
- final String existingUpdateOwner = sourceInfo.getUpdateOwnerPackageName();
- return getApplicationLabel(existingUpdateOwner);
- } catch (NameNotFoundException e) {
- return null;
- }
- }
-
- private CharSequence getApplicationLabel(String packageName) {
- try {
- final ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName,
- ApplicationInfoFlags.of(0));
- return mPackageManager.getApplicationLabel(appInfo);
- } catch (NameNotFoundException e) {
- return null;
- }
- }
-
- private boolean isAppUpdating(PackageInfo newPkgInfo) {
- String pkgName = newPkgInfo.packageName;
- // Check if there is already a package on the device with this name
- // but it has been renamed to something else.
- String[] oldName = mPackageManager.canonicalToCurrentPackageNames(new String[]{pkgName});
- if (oldName != null && oldName.length > 0 && oldName[0] != null) {
- pkgName = oldName[0];
- newPkgInfo.packageName = pkgName;
- newPkgInfo.applicationInfo.packageName = pkgName;
- }
- // Check if package is already installed. display confirmation dialog if replacing pkg
- try {
- // This is a little convoluted because we want to get all uninstalled
- // apps, but this may include apps with just data, and if it is just
- // data we still want to count it as "installed".
- ApplicationInfo appInfo = mPackageManager.getApplicationInfo(pkgName,
- PackageManager.MATCH_UNINSTALLED_PACKAGES);
- if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
- return false;
- }
- } catch (NameNotFoundException e) {
- return false;
- }
- return true;
- }
-
- /**
- * Once the user returns from Settings related to installing from unknown sources, reattempt
- * the installation if the source app is granted permission to install other apps. Abort the
- * installation if the source app is still not granted installing permission.
- * @return {@link InstallUserActionRequired} containing data required to ask user confirmation
- * to proceed with the install.
- * {@link InstallAborted} if there was an error while recomputing, or the source still
- * doesn't have install permission.
- */
- public InstallStage reattemptInstall() {
- InstallStage unknownSourceStage = handleUnknownSources(mAppOpRequestInfo);
- if (unknownSourceStage.getStageCode() == InstallStage.STAGE_READY) {
- // Source app now has appOp granted.
- return generateConfirmationSnippet();
- } else if (unknownSourceStage.getStageCode() == InstallStage.STAGE_ABORTED) {
- // There was some error in determining the AppOp code for the source app.
- // Abort installation
- return unknownSourceStage;
- } else {
- // AppOpsManager again returned a MODE_ERRORED or MODE_DEFAULT op code. This was
- // unexpected while reattempting the install. Let's abort it.
- Log.e(TAG, "AppOp still not granted.");
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
- }
-
- private InstallStage handleUnknownSources(AppOpRequestInfo requestInfo) {
- if (requestInfo.getCallingPackage() == null) {
- Log.i(TAG, "No source found for package " + mNewPackageInfo.packageName);
- return new InstallUserActionRequired.Builder(
- USER_ACTION_REASON_ANONYMOUS_SOURCE, null)
- .build();
- }
- // Shouldn't use static constant directly, see b/65534401.
- final String appOpStr =
- AppOpsManager.permissionToOp(Manifest.permission.REQUEST_INSTALL_PACKAGES);
- final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpStr,
- requestInfo.getOriginatingUid(),
- requestInfo.getCallingPackage(), requestInfo.getAttributionTag(),
- "Started package installation activity");
-
- if (mLocalLOGV) {
- Log.i(TAG, "handleUnknownSources(): appMode=" + appOpMode);
- }
- switch (appOpMode) {
- case AppOpsManager.MODE_DEFAULT:
- mAppOpsManager.setMode(appOpStr, requestInfo.getOriginatingUid(),
- requestInfo.getCallingPackage(), AppOpsManager.MODE_ERRORED);
- // fall through
- case AppOpsManager.MODE_ERRORED:
- try {
- ApplicationInfo sourceInfo =
- mPackageManager.getApplicationInfo(requestInfo.getCallingPackage(), 0);
- AppSnippet sourceAppSnippet = getAppSnippet(mContext, sourceInfo);
- return new InstallUserActionRequired.Builder(
- USER_ACTION_REASON_UNKNOWN_SOURCE, sourceAppSnippet)
- .setDialogMessage(requestInfo.getCallingPackage())
- .build();
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Did not find appInfo for " + requestInfo.getCallingPackage());
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
- case AppOpsManager.MODE_ALLOWED:
- return new InstallReady();
- default:
- Log.e(TAG, "Invalid app op mode " + appOpMode
- + " for OP_REQUEST_INSTALL_PACKAGES found for uid "
- + requestInfo.getOriginatingUid());
- return new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR).build();
- }
- }
-
-
- /**
- * Kick off the installation. Register a broadcast listener to get the result of the
- * installation and commit the staged session here. If the installation was session based,
- * signal the PackageInstaller that the user has granted permission to proceed with the install
- */
- public void initiateInstall() {
- if (mSessionId > 0) {
- mPackageInstaller.setPermissionsResult(mSessionId, true);
- mInstallResult.setValue(new InstallAborted.Builder(ABORT_REASON_DONE)
- .setActivityResultCode(Activity.RESULT_OK).build());
- return;
- }
-
- Uri uri = mIntent.getData();
- if (uri != null && SCHEME_PACKAGE.equals(uri.getScheme())) {
- try {
- mPackageManager.installExistingPackage(mNewPackageInfo.packageName);
- setStageBasedOnResult(PackageInstaller.STATUS_SUCCESS, -1, null, -1);
- } catch (PackageManager.NameNotFoundException e) {
- setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
- }
- return;
- }
-
- if (mStagedSessionId <= 0) {
- // How did we even land here?
- Log.e(TAG, "Invalid local session and caller initiated session");
- mInstallResult.setValue(new InstallAborted.Builder(ABORT_REASON_INTERNAL_ERROR)
- .build());
- return;
- }
-
- int installId;
- try {
- mInstallResult.setValue(new InstallInstalling(mAppSnippet));
- installId = InstallEventReceiver.addObserver(mContext,
- EventResultPersister.GENERATE_NEW_ID, this::setStageBasedOnResult);
- } catch (EventResultPersister.OutOfIdsException e) {
- setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
- return;
- }
-
- Intent broadcastIntent = new Intent(BROADCAST_ACTION);
- broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- broadcastIntent.setPackage(mContext.getPackageName());
- broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, installId);
-
- PendingIntent pendingIntent = PendingIntent.getBroadcast(
- mContext, installId, broadcastIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
-
- try {
- PackageInstaller.Session session = mPackageInstaller.openSession(mStagedSessionId);
- session.commit(pendingIntent.getIntentSender());
- } catch (Exception e) {
- Log.e(TAG, "Session " + mStagedSessionId + " could not be opened.", e);
- mPackageInstaller.abandonSession(mStagedSessionId);
- setStageBasedOnResult(PackageInstaller.STATUS_FAILURE,
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null, -1);
- }
- }
-
- private void setStageBasedOnResult(int statusCode, int legacyStatus, String message,
- int serviceId) {
- if (statusCode == PackageInstaller.STATUS_SUCCESS) {
- boolean shouldReturnResult = mIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
-
- InstallSuccess.Builder successBuilder = new InstallSuccess.Builder(mAppSnippet)
- .setShouldReturnResult(shouldReturnResult);
- Intent resultIntent;
- if (shouldReturnResult) {
- resultIntent = new Intent()
- .putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_SUCCEEDED);
- } else {
- resultIntent = mPackageManager
- .getLaunchIntentForPackage(mNewPackageInfo.packageName);
- }
- successBuilder.setResultIntent(resultIntent);
-
- mInstallResult.setValue(successBuilder.build());
- } else {
- mInstallResult.setValue(
- new InstallFailed(mAppSnippet, statusCode, legacyStatus, message));
- }
- }
-
- public MutableLiveData<InstallStage> getInstallResult() {
- return mInstallResult;
- }
-
- /**
- * Cleanup the staged session. Also signal the packageinstaller that an install session is to
- * be aborted
- */
- public void cleanupInstall() {
- if (mSessionId > 0) {
- mPackageInstaller.setPermissionsResult(mSessionId, false);
- } else if (mStagedSessionId > 0) {
- cleanupStagingSession();
- }
- }
-
- /**
- * When the identity of the install source could not be determined, user can skip checking the
- * source and directly proceed with the install.
- */
- public InstallStage forcedSkipSourceCheck() {
- return generateConfirmationSnippet();
- }
-
- public MutableLiveData<Integer> getStagingProgress() {
- if (mSessionStager != null) {
- return mSessionStager.getProgress();
- }
- return new MutableLiveData<>(0);
- }
-
- public MutableLiveData<InstallStage> getStagingResult() {
- return mStagingResult;
- }
-
- public interface SessionStageListener {
-
- void onStagingSuccess(SessionInfo info);
-
- void onStagingFailure();
- }
-
- public static class CallerInfo {
-
- private final String mPackageName;
- private final int mUid;
-
- public CallerInfo(String packageName, int uid) {
- mPackageName = packageName;
- mUid = uid;
- }
-
- public String getPackageName() {
- return mPackageName;
- }
-
- public int getUid() {
- return mUid;
- }
- }
-
- public static class AppOpRequestInfo {
-
- private String mCallingPackage;
- private String mAttributionTag;
- private int mOrginatingUid;
-
- public AppOpRequestInfo(String callingPackage, int orginatingUid, String attributionTag) {
- mCallingPackage = callingPackage;
- mOrginatingUid = orginatingUid;
- mAttributionTag = attributionTag;
- }
-
- public String getCallingPackage() {
- return mCallingPackage;
- }
-
- public String getAttributionTag() {
- return mAttributionTag;
- }
-
- public int getOriginatingUid() {
- return mOrginatingUid;
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
new file mode 100644
index 0000000..326e533
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -0,0 +1,867 @@
+/*
+ * 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.packageinstaller.v2.model
+
+import android.Manifest
+import android.app.Activity
+import android.app.AppOpsManager
+import android.app.PendingIntent
+import android.app.admin.DevicePolicyManager
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageInstaller.SessionInfo
+import android.content.pm.PackageInstaller.SessionParams
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.ParcelFileDescriptor
+import android.os.Process
+import android.os.UserManager
+import android.text.TextUtils
+import android.util.EventLog
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.R
+import com.android.packageinstaller.common.EventResultPersister
+import com.android.packageinstaller.common.EventResultPersister.OutOfIdsException
+import com.android.packageinstaller.common.InstallEventReceiver
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.ABORT_REASON_DONE
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.ABORT_REASON_INTERNAL_ERROR
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.ABORT_REASON_POLICY
+import com.android.packageinstaller.v2.model.InstallAborted.Companion.DLG_PACKAGE_ERROR
+import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_ANONYMOUS_SOURCE
+import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_INSTALL_CONFIRMATION
+import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_UNKNOWN_SOURCE
+import com.android.packageinstaller.v2.model.PackageUtil.canPackageQuery
+import com.android.packageinstaller.v2.model.PackageUtil.generateStubPackageInfo
+import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
+import com.android.packageinstaller.v2.model.PackageUtil.getPackageInfo
+import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
+import com.android.packageinstaller.v2.model.PackageUtil.isCallerSessionOwner
+import com.android.packageinstaller.v2.model.PackageUtil.isInstallPermissionGrantedOrRequested
+import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
+import java.io.File
+import java.io.IOException
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+
+class InstallRepository(private val context: Context) {
+
+ private val packageManager: PackageManager = context.packageManager
+ private val packageInstaller: PackageInstaller = packageManager.packageInstaller
+ private val userManager: UserManager? = context.getSystemService(UserManager::class.java)
+ private val devicePolicyManager: DevicePolicyManager? =
+ context.getSystemService(DevicePolicyManager::class.java)
+ private val appOpsManager: AppOpsManager? = context.getSystemService(AppOpsManager::class.java)
+ private val localLOGV = false
+ private var isSessionInstall = false
+ private var isTrustedSource = false
+ private val _stagingResult = MutableLiveData<InstallStage>()
+ val stagingResult: LiveData<InstallStage>
+ get() = _stagingResult
+ private val _installResult = MutableLiveData<InstallStage>()
+ val installResult: LiveData<InstallStage>
+ get() = _installResult
+
+ /**
+ * Session ID for a session created when caller uses PackageInstaller APIs
+ */
+ private var sessionId = SessionInfo.INVALID_ID
+
+ /**
+ * Session ID for a session created by this app
+ */
+ var stagedSessionId = SessionInfo.INVALID_ID
+ private set
+ private var callingUid = Process.INVALID_UID
+ private var callingPackage: String? = null
+ private var sessionStager: SessionStager? = null
+ private lateinit var intent: Intent
+ private lateinit var appOpRequestInfo: AppOpRequestInfo
+ private lateinit var appSnippet: PackageUtil.AppSnippet
+
+ /**
+ * PackageInfo of the app being installed on device.
+ */
+ private var newPackageInfo: PackageInfo? = null
+
+ /**
+ * Extracts information from the incoming install intent, checks caller's permission to install
+ * packages, verifies that the caller is the install session owner (in case of a session based
+ * install) and checks if the current user has restrictions set that prevent app installation,
+ *
+ * @param intent the incoming [Intent] object for installing a package
+ * @param callerInfo [CallerInfo] that holds the callingUid and callingPackageName
+ * @return
+ * * [InstallAborted] if there are errors while performing the checks
+ * * [InstallStaging] after successfully performing the checks
+ */
+ fun performPreInstallChecks(intent: Intent, callerInfo: CallerInfo): InstallStage {
+ this.intent = intent
+
+ var callingAttributionTag: String? = null
+
+ isSessionInstall =
+ PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL == intent.action
+ || PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action
+
+ sessionId = if (isSessionInstall)
+ intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, SessionInfo.INVALID_ID)
+ else SessionInfo.INVALID_ID
+
+ stagedSessionId = intent.getIntExtra(EXTRA_STAGED_SESSION_ID, SessionInfo.INVALID_ID)
+
+ callingPackage = callerInfo.packageName
+
+ if (callingPackage == null && sessionId != SessionInfo.INVALID_ID) {
+ val sessionInfo: SessionInfo? = packageInstaller.getSessionInfo(sessionId)
+ callingPackage = sessionInfo?.getInstallerPackageName()
+ callingAttributionTag = sessionInfo?.getInstallerAttributionTag()
+ }
+
+ // Uid of the source package, coming from ActivityManager
+ callingUid = callerInfo.uid
+ if (callingUid == Process.INVALID_UID) {
+ Log.e(LOG_TAG, "Could not determine the launching uid.")
+ }
+ val sourceInfo: ApplicationInfo? = getSourceInfo(callingPackage)
+ // Uid of the source package, with a preference to uid from ApplicationInfo
+ val originatingUid = sourceInfo?.uid ?: callingUid
+ appOpRequestInfo = AppOpRequestInfo(
+ getPackageNameForUid(context, originatingUid, callingPackage),
+ originatingUid, callingAttributionTag
+ )
+
+ if (callingUid == Process.INVALID_UID && sourceInfo == null) {
+ // Caller's identity could not be determined. Abort the install
+ return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+
+ if ((sessionId != SessionInfo.INVALID_ID
+ && !isCallerSessionOwner(packageInstaller, originatingUid, sessionId))
+ || (stagedSessionId != SessionInfo.INVALID_ID
+ && !isCallerSessionOwner(packageInstaller, Process.myUid(), stagedSessionId))
+ ) {
+ return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+
+ isTrustedSource = isInstallRequestFromTrustedSource(sourceInfo, this.intent, originatingUid)
+ if (!isInstallPermissionGrantedOrRequested(
+ context, callingUid, originatingUid, isTrustedSource
+ )
+ ) {
+ return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+
+ val restriction = getDevicePolicyRestrictions()
+ if (restriction != null) {
+ val adminSupportDetailsIntent =
+ devicePolicyManager!!.createAdminSupportIntent(restriction)
+ return InstallAborted(
+ ABORT_REASON_POLICY, message = restriction, resultIntent = adminSupportDetailsIntent
+ )
+ }
+
+ maybeRemoveInvalidInstallerPackageName(callerInfo)
+
+ return InstallStaging()
+ }
+
+ /**
+ * @return the ApplicationInfo for the installation source (the calling package), if available
+ */
+ private fun getSourceInfo(callingPackage: String?): ApplicationInfo? {
+ return try {
+ callingPackage?.let { packageManager.getApplicationInfo(it, 0) }
+ } catch (ignored: PackageManager.NameNotFoundException) {
+ null
+ }
+ }
+
+ private fun isInstallRequestFromTrustedSource(
+ sourceInfo: ApplicationInfo?,
+ intent: Intent,
+ originatingUid: Int,
+ ): Boolean {
+ val isNotUnknownSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)
+ return (sourceInfo != null && sourceInfo.isPrivilegedApp
+ && (isNotUnknownSource
+ || isPermissionGranted(context, Manifest.permission.INSTALL_PACKAGES, originatingUid)))
+ }
+
+ private fun getDevicePolicyRestrictions(): String? {
+ val restrictions = arrayOf(
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
+ )
+ for (restriction in restrictions) {
+ if (!userManager!!.hasUserRestrictionForUser(restriction, Process.myUserHandle())) {
+ continue
+ }
+ return restriction
+ }
+ return null
+ }
+
+ private fun maybeRemoveInvalidInstallerPackageName(callerInfo: CallerInfo) {
+ val installerPackageNameFromIntent =
+ intent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME) ?: return
+
+ if (!TextUtils.equals(installerPackageNameFromIntent, callerInfo.packageName)
+ && callerInfo.packageName != null
+ && isPermissionGranted(
+ packageManager, Manifest.permission.INSTALL_PACKAGES, callerInfo.packageName
+ )
+ ) {
+ Log.e(
+ LOG_TAG, "The given installer package name $installerPackageNameFromIntent"
+ + " is invalid. Remove it."
+ )
+ EventLog.writeEvent(
+ 0x534e4554, "236687884", callerInfo.uid,
+ "Invalid EXTRA_INSTALLER_PACKAGE_NAME"
+ )
+ intent.removeExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME)
+ }
+ }
+
+ @OptIn(DelicateCoroutinesApi::class)
+ fun stageForInstall() {
+ val uri = intent.data
+ if (stagedSessionId != SessionInfo.INVALID_ID
+ || isSessionInstall
+ || (uri != null && SCHEME_PACKAGE == uri.scheme)
+ ) {
+ // For a session based install or installing with a package:// URI, there is no file
+ // for us to stage.
+ _stagingResult.value = InstallReady()
+ return
+ }
+ if (uri != null
+ && ContentResolver.SCHEME_CONTENT == uri.scheme
+ && canPackageQuery(context, callingUid, uri)
+ ) {
+ if (stagedSessionId > 0) {
+ val info: SessionInfo? = packageInstaller.getSessionInfo(stagedSessionId)
+ if (info == null || !info.isActive || info.resolvedBaseApkPath == null) {
+ Log.w(LOG_TAG, "Session $stagedSessionId in funky state; ignoring")
+ if (info != null) {
+ cleanupStagingSession()
+ }
+ stagedSessionId = 0
+ }
+ }
+
+ // Session does not exist, or became invalid.
+ if (stagedSessionId <= 0) {
+ // Create session here to be able to show error.
+ try {
+ context.contentResolver.openAssetFileDescriptor(uri, "r").use { afd ->
+ val pfd: ParcelFileDescriptor? = afd?.parcelFileDescriptor
+ val params: SessionParams =
+ createSessionParams(intent, pfd, uri.toString())
+ stagedSessionId = packageInstaller.createSession(params)
+ }
+ } catch (e: IOException) {
+ Log.w(LOG_TAG, "Failed to create a staging session", e)
+ _stagingResult.value = InstallAborted(
+ ABORT_REASON_INTERNAL_ERROR,
+ resultIntent = Intent().putExtra(
+ Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_APK
+ ),
+ activityResultCode = Activity.RESULT_FIRST_USER
+ )
+ return
+ }
+ }
+
+ sessionStager = SessionStager(context, uri, stagedSessionId)
+ GlobalScope.launch(Dispatchers.Main) {
+ val wasFileStaged = sessionStager!!.execute()
+
+ if (wasFileStaged) {
+ _stagingResult.value = InstallReady()
+ } else {
+ cleanupStagingSession()
+ _stagingResult.value = InstallAborted(
+ ABORT_REASON_INTERNAL_ERROR,
+ resultIntent = Intent().putExtra(
+ Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_APK
+ ),
+ activityResultCode = Activity.RESULT_FIRST_USER
+ )
+ }
+ }
+ }
+ }
+
+ private fun cleanupStagingSession() {
+ if (stagedSessionId > 0) {
+ try {
+ packageInstaller.abandonSession(stagedSessionId)
+ } catch (ignored: SecurityException) {
+ }
+ stagedSessionId = 0
+ }
+ }
+
+ private fun createSessionParams(
+ intent: Intent,
+ pfd: ParcelFileDescriptor?,
+ debugPathName: String,
+ ): SessionParams {
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
+ val referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER, Uri::class.java)
+ params.setPackageSource(
+ if (referrerUri != null)
+ PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
+ else PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+ )
+ params.setInstallAsInstantApp(false)
+ params.setReferrerUri(referrerUri)
+ params.setOriginatingUri(
+ intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI, Uri::class.java)
+ )
+ params.setOriginatingUid(
+ intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID, Process.INVALID_UID)
+ )
+ params.setInstallerPackageName(intent.getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME))
+ params.setInstallReason(PackageManager.INSTALL_REASON_USER)
+ // Disable full screen intent usage by for sideloads.
+ params.setPermissionState(
+ Manifest.permission.USE_FULL_SCREEN_INTENT, SessionParams.PERMISSION_STATE_DENIED
+ )
+ if (pfd != null) {
+ try {
+ val installInfo = packageInstaller.readInstallInfo(pfd, debugPathName, 0)
+ params.setAppPackageName(installInfo.packageName)
+ params.setInstallLocation(installInfo.installLocation)
+ params.setSize(installInfo.calculateInstalledSize(params, pfd))
+ } catch (e: PackageInstaller.PackageParsingException) {
+ Log.e(LOG_TAG, "Cannot parse package $debugPathName. Assuming defaults.", e)
+ params.setSize(pfd.statSize)
+ } catch (e: IOException) {
+ Log.e(LOG_TAG, "Cannot calculate installed size $debugPathName. " +
+ "Try only apk size.", e
+ )
+ }
+ } else {
+ Log.e(LOG_TAG, "Cannot parse package $debugPathName. Assuming defaults.")
+ }
+ return params
+ }
+
+ /**
+ * Processes Install session, file:// or package:// URI to generate data pertaining to user
+ * confirmation for an install. This method also checks if the source app has the AppOp granted
+ * to install unknown apps. If an AppOp is to be requested, cache the user action prompt data to
+ * be reused once appOp has been granted
+ *
+ * @return
+ * * [InstallAborted]
+ * * If install session is invalid (not sealed or resolvedBaseApk path is invalid)
+ * * Source app doesn't have visibility to target app
+ * * The APK is invalid
+ * * URI is invalid
+ * * Can't get ApplicationInfo for source app, to request AppOp
+ *
+ * * [InstallUserActionRequired]
+ * * If AppOP is granted and user action is required to proceed with install
+ * * If AppOp grant is to be requested from the user
+ */
+ fun requestUserConfirmation(): InstallStage {
+ return if (isTrustedSource) {
+ if (localLOGV) {
+ Log.i(LOG_TAG, "install allowed")
+ }
+ // Returns InstallUserActionRequired stage if install details could be successfully
+ // computed, else it returns InstallAborted.
+ generateConfirmationSnippet()
+ } else {
+ val unknownSourceStage = handleUnknownSources(appOpRequestInfo)
+ if (unknownSourceStage.stageCode == InstallStage.STAGE_READY) {
+ // Source app already has appOp granted.
+ generateConfirmationSnippet()
+ } else {
+ unknownSourceStage
+ }
+ }
+ }
+
+ private fun generateConfirmationSnippet(): InstallStage {
+ val packageSource: Any?
+ val pendingUserActionReason: Int
+
+ if (PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action) {
+ val info = packageInstaller.getSessionInfo(sessionId)
+ val resolvedPath = info?.resolvedBaseApkPath
+ if (info == null || !info.isSealed || resolvedPath == null) {
+ Log.w(LOG_TAG, "Session $sessionId in funky state; ignoring")
+ return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ packageSource = Uri.fromFile(File(resolvedPath))
+ // TODO: Not sure where is this used yet. PIA.java passes it to
+ // InstallInstalling if not null
+ // mOriginatingURI = null;
+ // mReferrerURI = null;
+ pendingUserActionReason = info.getPendingUserActionReason()
+ } else if (PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL == intent.action) {
+ val info = packageInstaller.getSessionInfo(sessionId)
+ if (info == null || !info.isPreApprovalRequested) {
+ Log.w(LOG_TAG, "Session $sessionId in funky state; ignoring")
+ return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ packageSource = info
+ // mOriginatingURI = null;
+ // mReferrerURI = null;
+ pendingUserActionReason = info.getPendingUserActionReason()
+ } else {
+ // Two possible origins:
+ // 1. Installation with SCHEME_PACKAGE.
+ // 2. Installation with "file://" for session created by this app
+ packageSource =
+ if (intent.data?.scheme == SCHEME_PACKAGE) {
+ intent.data
+ } else {
+ val stagedSessionInfo = packageInstaller.getSessionInfo(stagedSessionId)
+ Uri.fromFile(File(stagedSessionInfo?.resolvedBaseApkPath!!))
+ }
+ // mOriginatingURI = mIntent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+ // mReferrerURI = mIntent.getParcelableExtra(Intent.EXTRA_REFERRER);
+ pendingUserActionReason = PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE
+ }
+
+ // if there's nothing to do, quietly slip into the ether
+ if (packageSource == null) {
+ Log.w(LOG_TAG, "Unspecified source")
+ return InstallAborted(
+ ABORT_REASON_INTERNAL_ERROR,
+ resultIntent = Intent().putExtra(
+ Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_URI
+ ),
+ activityResultCode = Activity.RESULT_FIRST_USER
+ )
+ }
+ return processAppSnippet(packageSource, pendingUserActionReason)
+ }
+
+ /**
+ * Parse the Uri (post-commit install session) or use the SessionInfo (pre-commit install
+ * session) to set up the installer for this install.
+ *
+ * @param source The source of package URI or SessionInfo
+ * @return
+ * * [InstallUserActionRequired] if source could be processed
+ * * [InstallAborted] if source is invalid or there was an error is processing a source
+ */
+ private fun processAppSnippet(source: Any, userActionReason: Int): InstallStage {
+ return when (source) {
+ is Uri -> processPackageUri(source, userActionReason)
+ is SessionInfo -> processSessionInfo(source, userActionReason)
+ else -> InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ }
+
+ /**
+ * Parse the Uri and set up the installer for this package.
+ *
+ * @param packageUri The URI to parse
+ * @return
+ * * [InstallUserActionRequired] if source could be processed
+ * * [InstallAborted] if source is invalid or there was an error is processing a source
+ */
+ private fun processPackageUri(packageUri: Uri, userActionReason: Int): InstallStage {
+ val scheme = packageUri.scheme
+ val packageName = packageUri.schemeSpecificPart
+ if (scheme == null) {
+ return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ if (localLOGV) {
+ Log.i(LOG_TAG, "processPackageUri(): uri = $packageUri, scheme = $scheme")
+ }
+ when (scheme) {
+ SCHEME_PACKAGE -> {
+ for (handle in userManager!!.getUserHandles(true)) {
+ val pmForUser = context.createContextAsUser(handle, 0).packageManager
+ try {
+ if (pmForUser.canPackageQuery(callingPackage!!, packageName)) {
+ newPackageInfo = pmForUser.getPackageInfo(
+ packageName,
+ PackageManager.GET_PERMISSIONS
+ or PackageManager.MATCH_UNINSTALLED_PACKAGES
+ )
+ }
+ } catch (ignored: PackageManager.NameNotFoundException) {
+ }
+ }
+ if (newPackageInfo == null) {
+ Log.w(
+ LOG_TAG, "Requested package " + packageUri.schemeSpecificPart
+ + " not available. Discontinuing installation"
+ )
+ return InstallAborted(
+ ABORT_REASON_INTERNAL_ERROR,
+ errorDialogType = DLG_PACKAGE_ERROR,
+ resultIntent = Intent().putExtra(
+ Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_APK
+ ),
+ activityResultCode = Activity.RESULT_FIRST_USER
+ )
+ }
+ appSnippet = getAppSnippet(context, newPackageInfo!!)
+ if (localLOGV) {
+ Log.i(LOG_TAG, "Created snippet for " + appSnippet.label)
+ }
+ }
+
+ ContentResolver.SCHEME_FILE -> {
+ val sourceFile = packageUri.path?.let { File(it) }
+ newPackageInfo = sourceFile?.let {
+ getPackageInfo(context, it, PackageManager.GET_PERMISSIONS)
+ }
+
+ // Check for parse errors
+ if (newPackageInfo == null) {
+ Log.w(
+ LOG_TAG, "Parse error when parsing manifest. " +
+ "Discontinuing installation"
+ )
+ return InstallAborted(
+ ABORT_REASON_INTERNAL_ERROR,
+ errorDialogType = DLG_PACKAGE_ERROR,
+ resultIntent = Intent().putExtra(
+ Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_APK
+ ),
+ activityResultCode = Activity.RESULT_FIRST_USER
+ )
+ }
+ if (localLOGV) {
+ Log.i(LOG_TAG, "Creating snippet for local file $sourceFile")
+ }
+ appSnippet = getAppSnippet(context, newPackageInfo!!, sourceFile!!)
+ }
+
+ else -> {
+ Log.e(LOG_TAG, "Unexpected URI scheme $packageUri")
+ return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ }
+ return InstallUserActionRequired(
+ USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet, isAppUpdating(newPackageInfo!!),
+ getUpdateMessage(newPackageInfo!!, userActionReason)
+ )
+ }
+
+ /**
+ * Use the SessionInfo and set up the installer for pre-commit install session.
+ *
+ * @param sessionInfo The SessionInfo to compose
+ * @return
+ * * [InstallUserActionRequired] if source could be processed
+ * * [InstallAborted] if source is invalid or there was an error is processing a source
+ */
+ private fun processSessionInfo(sessionInfo: SessionInfo, userActionReason: Int): InstallStage {
+ newPackageInfo = generateStubPackageInfo(sessionInfo.getAppPackageName())
+ appSnippet = getAppSnippet(context, sessionInfo)
+
+ return InstallUserActionRequired(
+ USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet, isAppUpdating(newPackageInfo!!),
+ getUpdateMessage(newPackageInfo!!, userActionReason)
+
+ )
+ }
+
+ private fun getUpdateMessage(pkgInfo: PackageInfo, userActionReason: Int): String? {
+ if (isAppUpdating(pkgInfo)) {
+ val existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo)
+ val requestedUpdateOwnerLabel = getApplicationLabel(callingPackage)
+ if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
+ && userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP
+ ) {
+ return context.getString(
+ R.string.install_confirm_question_update_owner_reminder,
+ requestedUpdateOwnerLabel, existingUpdateOwnerLabel
+ )
+ }
+ }
+ return null
+ }
+
+ private fun getExistingUpdateOwnerLabel(pkgInfo: PackageInfo): CharSequence? {
+ return try {
+ val packageName = pkgInfo.packageName
+ val sourceInfo = packageManager.getInstallSourceInfo(packageName)
+ val existingUpdateOwner = sourceInfo.updateOwnerPackageName
+ getApplicationLabel(existingUpdateOwner)
+ } catch (e: PackageManager.NameNotFoundException) {
+ null
+ }
+ }
+
+ private fun getApplicationLabel(packageName: String?): CharSequence? {
+ return try {
+ val appInfo = packageName?.let {
+ packageManager.getApplicationInfo(
+ it, PackageManager.ApplicationInfoFlags.of(0)
+ )
+ }
+ appInfo?.let { packageManager.getApplicationLabel(it) }
+ } catch (e: PackageManager.NameNotFoundException) {
+ null
+ }
+ }
+
+ private fun isAppUpdating(newPkgInfo: PackageInfo): Boolean {
+ var pkgName = newPkgInfo.packageName
+ // Check if there is already a package on the device with this name
+ // but it has been renamed to something else.
+ val oldName = packageManager.canonicalToCurrentPackageNames(arrayOf(pkgName))
+ if (oldName != null && oldName.isNotEmpty() && oldName[0] != null) {
+ pkgName = oldName[0]
+ newPkgInfo.packageName = pkgName
+ newPkgInfo.applicationInfo?.packageName = pkgName
+ }
+
+ // Check if package is already installed. display confirmation dialog if replacing pkg
+ try {
+ // This is a little convoluted because we want to get all uninstalled
+ // apps, but this may include apps with just data, and if it is just
+ // data we still want to count it as "installed".
+ val appInfo = packageManager.getApplicationInfo(
+ pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES
+ )
+ if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
+ return false
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ return false
+ }
+ return true
+ }
+
+ /**
+ * Once the user returns from Settings related to installing from unknown sources, reattempt
+ * the installation if the source app is granted permission to install other apps. Abort the
+ * installation if the source app is still not granted installing permission.
+ *
+ * @return
+ * * [InstallUserActionRequired] containing data required to ask user confirmation
+ * to proceed with the install.
+ * * [InstallAborted] if there was an error while recomputing, or the source still
+ * doesn't have install permission.
+ */
+ fun reattemptInstall(): InstallStage {
+ val unknownSourceStage = handleUnknownSources(appOpRequestInfo)
+ return when (unknownSourceStage.stageCode) {
+ InstallStage.STAGE_READY -> {
+ // Source app now has appOp granted.
+ generateConfirmationSnippet()
+ }
+
+ InstallStage.STAGE_ABORTED -> {
+ // There was some error in determining the AppOp code for the source app.
+ // Abort installation
+ unknownSourceStage
+ }
+
+ else -> {
+ // AppOpsManager again returned a MODE_ERRORED or MODE_DEFAULT op code. This was
+ // unexpected while reattempting the install. Let's abort it.
+ Log.e(LOG_TAG, "AppOp still not granted.")
+ InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ }
+ }
+
+ private fun handleUnknownSources(requestInfo: AppOpRequestInfo): InstallStage {
+ if (requestInfo.callingPackage == null) {
+ Log.i(LOG_TAG, "No source found for package " + newPackageInfo?.packageName)
+ return InstallUserActionRequired(USER_ACTION_REASON_ANONYMOUS_SOURCE)
+ }
+ // Shouldn't use static constant directly, see b/65534401.
+ val appOpStr = AppOpsManager.permissionToOp(Manifest.permission.REQUEST_INSTALL_PACKAGES)
+ val appOpMode = appOpsManager!!.noteOpNoThrow(
+ appOpStr!!, requestInfo.originatingUid, requestInfo.callingPackage,
+ requestInfo.attributionTag, "Started package installation activity"
+ )
+ if (localLOGV) {
+ Log.i(LOG_TAG, "handleUnknownSources(): appMode=$appOpMode")
+ }
+
+ return when (appOpMode) {
+ AppOpsManager.MODE_DEFAULT, AppOpsManager.MODE_ERRORED -> {
+ if (appOpMode == AppOpsManager.MODE_DEFAULT) {
+ appOpsManager.setMode(
+ appOpStr, requestInfo.originatingUid, requestInfo.callingPackage,
+ AppOpsManager.MODE_ERRORED
+ )
+ }
+ try {
+ val sourceInfo =
+ packageManager.getApplicationInfo(requestInfo.callingPackage, 0)
+ val sourceAppSnippet = getAppSnippet(context, sourceInfo)
+ InstallUserActionRequired(
+ USER_ACTION_REASON_UNKNOWN_SOURCE, appSnippet = sourceAppSnippet,
+ dialogMessage = requestInfo.callingPackage
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(LOG_TAG, "Did not find appInfo for " + requestInfo.callingPackage)
+ InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ }
+
+ AppOpsManager.MODE_ALLOWED -> InstallReady()
+
+ else -> {
+ Log.e(
+ LOG_TAG, "Invalid app op mode $appOpMode for " +
+ "OP_REQUEST_INSTALL_PACKAGES found for uid $requestInfo.originatingUid"
+ )
+ InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ }
+ }
+ }
+
+ /**
+ * Kick off the installation. Register a broadcast listener to get the result of the
+ * installation and commit the staged session here. If the installation was session based,
+ * signal the PackageInstaller that the user has granted permission to proceed with the install
+ */
+ fun initiateInstall() {
+ if (sessionId > 0) {
+ packageInstaller.setPermissionsResult(sessionId, true)
+ _installResult.value = InstallAborted(
+ ABORT_REASON_DONE, activityResultCode = Activity.RESULT_OK
+ )
+ return
+ }
+ val uri = intent.data
+ if (SCHEME_PACKAGE == uri?.scheme) {
+ try {
+ packageManager.installExistingPackage(
+ newPackageInfo!!.packageName, PackageManager.INSTALL_REASON_USER
+ )
+ setStageBasedOnResult(PackageInstaller.STATUS_SUCCESS, -1, null)
+ } catch (e: PackageManager.NameNotFoundException) {
+ setStageBasedOnResult(
+ PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+ null)
+ }
+ return
+ }
+ if (stagedSessionId <= 0) {
+ // How did we even land here?
+ Log.e(LOG_TAG, "Invalid local session and caller initiated session")
+ _installResult.value = InstallAborted(ABORT_REASON_INTERNAL_ERROR)
+ return
+ }
+ val installId: Int
+ try {
+ _installResult.value = InstallInstalling(appSnippet)
+ installId = InstallEventReceiver.addObserver(
+ context, EventResultPersister.GENERATE_NEW_ID
+ ) { statusCode: Int, legacyStatus: Int, message: String?, serviceId: Int ->
+ setStageBasedOnResult(statusCode, legacyStatus, message)
+ }
+ } catch (e: OutOfIdsException) {
+ setStageBasedOnResult(
+ PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null)
+ return
+ }
+ val broadcastIntent = Intent(BROADCAST_ACTION)
+ broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ broadcastIntent.setPackage(context.packageName)
+ broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, installId)
+ val pendingIntent = PendingIntent.getBroadcast(
+ context, installId, broadcastIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ try {
+ val session = packageInstaller.openSession(stagedSessionId)
+ session.commit(pendingIntent.intentSender)
+ } catch (e: Exception) {
+ Log.e(LOG_TAG, "Session $stagedSessionId could not be opened.", e)
+ packageInstaller.abandonSession(stagedSessionId)
+ setStageBasedOnResult(
+ PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null)
+ }
+ }
+
+ private fun setStageBasedOnResult(
+ statusCode: Int,
+ legacyStatus: Int,
+ message: String?
+ ) {
+ if (statusCode == PackageInstaller.STATUS_SUCCESS) {
+ val shouldReturnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
+ val resultIntent = if (shouldReturnResult) {
+ Intent().putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_SUCCEEDED)
+ } else {
+ packageManager.getLaunchIntentForPackage(newPackageInfo!!.packageName)
+ }
+ _installResult.setValue(InstallSuccess(appSnippet, shouldReturnResult, resultIntent))
+ } else {
+ _installResult.setValue(InstallFailed(appSnippet, statusCode, legacyStatus, message))
+ }
+ }
+
+ /**
+ * Cleanup the staged session. Also signal the packageinstaller that an install session is to
+ * be aborted
+ */
+ fun cleanupInstall() {
+ if (sessionId > 0) {
+ packageInstaller.setPermissionsResult(sessionId, false)
+ } else if (stagedSessionId > 0) {
+ cleanupStagingSession()
+ }
+ }
+
+ /**
+ * When the identity of the install source could not be determined, user can skip checking the
+ * source and directly proceed with the install.
+ */
+ fun forcedSkipSourceCheck(): InstallStage {
+ return generateConfirmationSnippet()
+ }
+
+ val stagingProgress: LiveData<Int>
+ get() = sessionStager?.progress ?: MutableLiveData(0)
+
+ companion object {
+ const val EXTRA_STAGED_SESSION_ID = "com.android.packageinstaller.extra.STAGED_SESSION_ID"
+ const val SCHEME_PACKAGE = "package"
+ const val BROADCAST_ACTION = "com.android.packageinstaller.ACTION_INSTALL_COMMIT"
+ private val LOG_TAG = InstallRepository::class.java.simpleName
+ }
+
+ data class CallerInfo(val packageName: String?, val uid: Int)
+ data class AppOpRequestInfo(
+ val callingPackage: String?,
+ val originatingUid: Int,
+ val attributionTag: String?,
+ )
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt
new file mode 100644
index 0000000..be49b39
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallStages.kt
@@ -0,0 +1,134 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.packageinstaller.v2.model
+
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+
+sealed class InstallStage(val stageCode: Int) {
+
+ companion object {
+ const val STAGE_DEFAULT = -1
+ const val STAGE_ABORTED = 0
+ const val STAGE_STAGING = 1
+ const val STAGE_READY = 2
+ const val STAGE_USER_ACTION_REQUIRED = 3
+ const val STAGE_INSTALLING = 4
+ const val STAGE_SUCCESS = 5
+ const val STAGE_FAILED = 6
+ }
+}
+
+class InstallStaging : InstallStage(STAGE_STAGING)
+
+class InstallReady : InstallStage(STAGE_READY)
+
+data class InstallUserActionRequired(
+ val actionReason: Int,
+ private val appSnippet: PackageUtil.AppSnippet? = null,
+ val isAppUpdating: Boolean = false,
+ val dialogMessage: String? = null,
+) : InstallStage(STAGE_USER_ACTION_REQUIRED) {
+
+ val appIcon: Drawable?
+ get() = appSnippet?.icon
+
+ val appLabel: String?
+ get() = appSnippet?.let { appSnippet.label as String? }
+
+ companion object {
+ const val USER_ACTION_REASON_UNKNOWN_SOURCE = 0
+ const val USER_ACTION_REASON_ANONYMOUS_SOURCE = 1
+ const val USER_ACTION_REASON_INSTALL_CONFIRMATION = 2
+ }
+}
+
+data class InstallInstalling(private val appSnippet: PackageUtil.AppSnippet) :
+ InstallStage(STAGE_INSTALLING) {
+
+ val appIcon: Drawable?
+ get() = appSnippet.icon
+
+ val appLabel: String?
+ get() = appSnippet.label as String?
+}
+
+data class InstallSuccess(
+ private val appSnippet: PackageUtil.AppSnippet,
+ val shouldReturnResult: Boolean = false,
+ /**
+ *
+ * * If the caller is requesting a result back, this will hold the Intent with
+ * [Intent.EXTRA_INSTALL_RESULT] set to [PackageManager.INSTALL_SUCCEEDED] which is sent
+ * back to the caller.
+ *
+ * * If the caller doesn't want the result back, this will hold the Intent that launches
+ * the newly installed / updated app if a launchable activity exists.
+ */
+ val resultIntent: Intent? = null,
+) : InstallStage(STAGE_SUCCESS) {
+
+ val appIcon: Drawable?
+ get() = appSnippet.icon
+
+ val appLabel: String?
+ get() = appSnippet.label as String?
+}
+
+data class InstallFailed(
+ private val appSnippet: PackageUtil.AppSnippet,
+ val legacyCode: Int,
+ val statusCode: Int,
+ val message: String?,
+) : InstallStage(STAGE_FAILED) {
+
+ val appIcon: Drawable?
+ get() = appSnippet.icon
+
+ val appLabel: String?
+ get() = appSnippet.label as String?
+}
+
+data class InstallAborted(
+ val abortReason: Int,
+ /**
+ * It will hold the restriction name, when the restriction was enforced by the system, and not
+ * a device admin.
+ */
+ val message: String? = null,
+ /**
+ * * If abort reason is [ABORT_REASON_POLICY], then this will hold the Intent
+ * to display a support dialog when a feature was disabled by an admin. It will be
+ * `null` if the feature is disabled by the system. In this case, the restriction name
+ * will be set in [message]
+ * * If the abort reason is [ABORT_REASON_INTERNAL_ERROR], it **may** hold an
+ * intent to be sent as a result to the calling activity.
+ */
+ val resultIntent: Intent? = null,
+ val activityResultCode: Int = Activity.RESULT_CANCELED,
+ val errorDialogType: Int? = 0,
+) : InstallStage(STAGE_ABORTED) {
+
+ companion object {
+ const val ABORT_REASON_INTERNAL_ERROR = 0
+ const val ABORT_REASON_POLICY = 1
+ const val ABORT_REASON_DONE = 2
+ const val DLG_PACKAGE_ERROR = 1
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java
deleted file mode 100644
index fe05237..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.java
+++ /dev/null
@@ -1,462 +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.packageinstaller.v2.model;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import java.io.File;
-import java.util.Arrays;
-import java.util.Objects;
-
-public class PackageUtil {
-
- private static final String TAG = InstallRepository.class.getSimpleName();
- private static final String DOWNLOADS_AUTHORITY = "downloads";
- private static final String SPLIT_BASE_APK_END_WITH = "base.apk";
-
- /**
- * Determines if the UID belongs to the system downloads provider and returns the
- * {@link ApplicationInfo} of the provider
- *
- * @param uid UID of the caller
- * @return {@link ApplicationInfo} of the provider if a downloads provider exists, it is a
- * system app, and its UID matches with the passed UID, null otherwise.
- */
- public static ApplicationInfo getSystemDownloadsProviderInfo(PackageManager pm, int uid) {
- final ProviderInfo providerInfo = pm.resolveContentProvider(
- DOWNLOADS_AUTHORITY, 0);
- if (providerInfo == null) {
- // There seems to be no currently enabled downloads provider on the system.
- return null;
- }
- ApplicationInfo appInfo = providerInfo.applicationInfo;
- if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && uid == appInfo.uid) {
- return appInfo;
- }
- return null;
- }
-
- /**
- * Get the maximum target sdk for a UID.
- *
- * @param context The context to use
- * @param uid The UID requesting the install/uninstall
- * @return The maximum target SDK or -1 if the uid does not match any packages.
- */
- public static int getMaxTargetSdkVersionForUid(@NonNull Context context, int uid) {
- PackageManager pm = context.getPackageManager();
- final String[] packages = pm.getPackagesForUid(uid);
- int targetSdkVersion = -1;
- if (packages != null) {
- for (String packageName : packages) {
- try {
- ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
- targetSdkVersion = Math.max(targetSdkVersion, info.targetSdkVersion);
- } catch (PackageManager.NameNotFoundException e) {
- // Ignore and try the next package
- }
- }
- }
- return targetSdkVersion;
- }
-
- public static boolean canPackageQuery(Context context, int callingUid, Uri packageUri) {
- PackageManager pm = context.getPackageManager();
- ProviderInfo info = pm.resolveContentProvider(packageUri.getAuthority(),
- PackageManager.ComponentInfoFlags.of(0));
- if (info == null) {
- return false;
- }
- String targetPackage = info.packageName;
-
- String[] callingPackages = pm.getPackagesForUid(callingUid);
- if (callingPackages == null) {
- return false;
- }
- for (String callingPackage : callingPackages) {
- try {
- if (pm.canPackageQuery(callingPackage, targetPackage)) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // no-op
- }
- }
- return false;
- }
-
- /**
- * @param context the {@link Context} object
- * @param permission the permission name to check
- * @param callingUid the UID of the caller who's permission is being checked
- * @return {@code true} if the callingUid is granted the said permission
- */
- public static boolean isPermissionGranted(Context context, String permission, int callingUid) {
- return context.checkPermission(permission, -1, callingUid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * @param pm the {@link PackageManager} object
- * @param permission the permission name to check
- * @param packageName the name of the package who's permission is being checked
- * @return {@code true} if the package is granted the said permission
- */
- public static boolean isPermissionGranted(PackageManager pm, String permission,
- String packageName) {
- return pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * @param context the {@link Context} object
- * @param callingUid the UID of the caller who's permission is being checked
- * @param originatingUid the UID from where install is being originated. This could be same as
- * callingUid or it will be the UID of the package performing a session based install
- * @param isTrustedSource whether install request is coming from a privileged app or an app that
- * has {@link Manifest.permission.INSTALL_PACKAGES} permission granted
- * @return {@code true} if the package is granted the said permission
- */
- public static boolean isInstallPermissionGrantedOrRequested(Context context, int callingUid,
- int originatingUid, boolean isTrustedSource) {
- boolean isDocumentsManager =
- isPermissionGranted(context, Manifest.permission.MANAGE_DOCUMENTS, callingUid);
- boolean isSystemDownloadsProvider =
- getSystemDownloadsProviderInfo(context.getPackageManager(), callingUid) != null;
-
- if (!isTrustedSource && !isSystemDownloadsProvider && !isDocumentsManager) {
-
- final int targetSdkVersion = getMaxTargetSdkVersionForUid(context, originatingUid);
- if (targetSdkVersion < 0) {
- // Invalid originating uid supplied. Abort install.
- Log.w(TAG, "Cannot get target sdk version for uid " + originatingUid);
- return false;
- } else if (targetSdkVersion >= Build.VERSION_CODES.O
- && !isUidRequestingPermission(context.getPackageManager(), originatingUid,
- Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
- Log.e(TAG, "Requesting uid " + originatingUid + " needs to declare permission "
- + Manifest.permission.REQUEST_INSTALL_PACKAGES);
- return false;
- }
- }
- return true;
- }
-
- /**
- * @param pm the {@link PackageManager} object
- * @param uid the UID of the caller who's permission is being checked
- * @param permission the permission name to check
- * @return {@code true} if the caller is requesting the said permission in its Manifest
- */
- public static boolean isUidRequestingPermission(PackageManager pm, int uid, String permission) {
- final String[] packageNames = pm.getPackagesForUid(uid);
- if (packageNames == null) {
- return false;
- }
- for (final String packageName : packageNames) {
- final PackageInfo packageInfo;
- try {
- packageInfo = pm.getPackageInfo(packageName,
- PackageManager.GET_PERMISSIONS);
- } catch (PackageManager.NameNotFoundException e) {
- // Ignore and try the next package
- continue;
- }
- if (packageInfo.requestedPermissions != null
- && Arrays.asList(packageInfo.requestedPermissions).contains(permission)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @param pi the {@link PackageInstaller} object to use
- * @param originatingUid the UID of the package performing a session based install
- * @param sessionId ID of the install session
- * @return {@code true} if the caller is the session owner
- */
- public static boolean isCallerSessionOwner(PackageInstaller pi, int originatingUid,
- int sessionId) {
- if (originatingUid == Process.ROOT_UID) {
- return true;
- }
- PackageInstaller.SessionInfo sessionInfo = pi.getSessionInfo(sessionId);
- if (sessionInfo == null) {
- return false;
- }
- int installerUid = sessionInfo.getInstallerUid();
- return originatingUid == installerUid;
- }
-
- /**
- * Generates a stub {@link PackageInfo} object for the given packageName
- */
- public static PackageInfo generateStubPackageInfo(String packageName) {
- final PackageInfo info = new PackageInfo();
- final ApplicationInfo aInfo = new ApplicationInfo();
- info.applicationInfo = aInfo;
- info.packageName = info.applicationInfo.packageName = packageName;
- return info;
- }
-
- /**
- * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
- * {@link SessionInfo} object
- */
- public static AppSnippet getAppSnippet(Context context, SessionInfo info) {
- PackageManager pm = context.getPackageManager();
- CharSequence label = info.getAppLabel();
- Drawable icon = info.getAppIcon() != null ?
- new BitmapDrawable(context.getResources(), info.getAppIcon())
- : pm.getDefaultActivityIcon();
- return new AppSnippet(label, icon);
- }
-
- /**
- * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
- * {@link PackageInfo} object
- */
- public static AppSnippet getAppSnippet(Context context, PackageInfo pkgInfo) {
- return getAppSnippet(context, pkgInfo.applicationInfo);
- }
-
- /**
- * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
- * {@link ApplicationInfo} object
- */
- public static AppSnippet getAppSnippet(Context context, ApplicationInfo appInfo) {
- PackageManager pm = context.getPackageManager();
- CharSequence label = pm.getApplicationLabel(appInfo);
- Drawable icon = pm.getApplicationIcon(appInfo);
- return new AppSnippet(label, icon);
- }
-
- /**
- * Generates an {@link AppSnippet} containing an appIcon and appLabel from the
- * supplied APK file
- */
- public static AppSnippet getAppSnippet(Context context, ApplicationInfo appInfo,
- File sourceFile) {
- ApplicationInfo appInfoFromFile = processAppInfoForFile(appInfo, sourceFile);
- CharSequence label = getAppLabelFromFile(context, appInfoFromFile);
- Drawable icon = getAppIconFromFile(context, appInfoFromFile);
- return new AppSnippet(label, icon);
- }
-
- /**
- * Utility method to load application label
- *
- * @param context context of package that can load the resources
- * @param appInfo ApplicationInfo object of package whose resources are to be loaded
- */
- public static CharSequence getAppLabelFromFile(Context context, ApplicationInfo appInfo) {
- PackageManager pm = context.getPackageManager();
- CharSequence label = null;
- // Try to load the label from the package's resources. If an app has not explicitly
- // specified any label, just use the package name.
- if (appInfo.labelRes != 0) {
- try {
- label = appInfo.loadLabel(pm);
- } catch (Resources.NotFoundException e) {
- }
- }
- if (label == null) {
- label = (appInfo.nonLocalizedLabel != null) ?
- appInfo.nonLocalizedLabel : appInfo.packageName;
- }
- return label;
- }
-
- /**
- * Utility method to load application icon
- *
- * @param context context of package that can load the resources
- * @param appInfo ApplicationInfo object of package whose resources are to be loaded
- */
- public static Drawable getAppIconFromFile(Context context, ApplicationInfo appInfo) {
- PackageManager pm = context.getPackageManager();
- Drawable icon = null;
- // Try to load the icon from the package's resources. If an app has not explicitly
- // specified any resource, just use the default icon for now.
- try {
- if (appInfo.icon != 0) {
- try {
- icon = appInfo.loadIcon(pm);
- } catch (Resources.NotFoundException e) {
- }
- }
- if (icon == null) {
- icon = context.getPackageManager().getDefaultActivityIcon();
- }
- } catch (OutOfMemoryError e) {
- Log.i(TAG, "Could not load app icon", e);
- }
- return icon;
- }
-
- private static ApplicationInfo processAppInfoForFile(ApplicationInfo appInfo, File sourceFile) {
- final String archiveFilePath = sourceFile.getAbsolutePath();
- appInfo.publicSourceDir = archiveFilePath;
-
- if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) {
- final File[] files = sourceFile.getParentFile().listFiles();
- final String[] splits = Arrays.stream(appInfo.splitNames)
- .map(i -> findFilePath(files, i + ".apk"))
- .filter(Objects::nonNull)
- .toArray(String[]::new);
-
- appInfo.splitSourceDirs = splits;
- appInfo.splitPublicSourceDirs = splits;
- }
- return appInfo;
- }
-
- private static String findFilePath(File[] files, String postfix) {
- for (File file : files) {
- final String path = file.getAbsolutePath();
- if (path.endsWith(postfix)) {
- return path;
- }
- }
- return null;
- }
-
- /**
- * @return the packageName corresponding to a UID.
- */
- public static String getPackageNameForUid(Context context, int sourceUid,
- String callingPackage) {
- if (sourceUid == Process.INVALID_UID) {
- return null;
- }
- // If the sourceUid belongs to the system downloads provider, we explicitly return the
- // name of the Download Manager package. This is because its UID is shared with multiple
- // packages, resulting in uncertainty about which package will end up first in the list
- // of packages associated with this UID
- PackageManager pm = context.getPackageManager();
- ApplicationInfo systemDownloadProviderInfo = getSystemDownloadsProviderInfo(
- pm, sourceUid);
- if (systemDownloadProviderInfo != null) {
- return systemDownloadProviderInfo.packageName;
- }
- String[] packagesForUid = pm.getPackagesForUid(sourceUid);
- if (packagesForUid == null) {
- return null;
- }
- if (packagesForUid.length > 1) {
- if (callingPackage != null) {
- for (String packageName : packagesForUid) {
- if (packageName.equals(callingPackage)) {
- return packageName;
- }
- }
- }
- Log.i(TAG, "Multiple packages found for source uid " + sourceUid);
- }
- return packagesForUid[0];
- }
-
- /**
- * Utility method to get package information for a given {@link File}
- */
- public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) {
- String filePath = sourceFile.getAbsolutePath();
- if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) {
- File dir = sourceFile.getParentFile();
- if (dir.listFiles().length > 1) {
- // split apks, use file directory to get archive info
- filePath = dir.getPath();
- }
- }
- try {
- return context.getPackageManager().getPackageArchiveInfo(filePath, flags);
- } catch (Exception ignored) {
- return null;
- }
- }
-
- /**
- * Is a profile part of a user?
- *
- * @param userManager The user manager
- * @param userHandle The handle of the user
- * @param profileHandle The handle of the profile
- *
- * @return If the profile is part of the user or the profile parent of the user
- */
- public static boolean isProfileOfOrSame(UserManager userManager, UserHandle userHandle,
- UserHandle profileHandle) {
- if (userHandle.equals(profileHandle)) {
- return true;
- }
- return userManager.getProfileParent(profileHandle) != null
- && userManager.getProfileParent(profileHandle).equals(userHandle);
- }
-
- /**
- * The class to hold an incoming package's icon and label.
- * See {@link #getAppSnippet(Context, SessionInfo)},
- * {@link #getAppSnippet(Context, PackageInfo)},
- * {@link #getAppSnippet(Context, ApplicationInfo)},
- * {@link #getAppSnippet(Context, ApplicationInfo, File)}
- */
- public static class AppSnippet {
-
- private CharSequence mLabel;
- private Drawable mIcon;
-
- public AppSnippet(CharSequence label, Drawable icon) {
- mLabel = label;
- mIcon = icon;
- }
-
- public AppSnippet() {
- }
-
- public CharSequence getLabel() {
- return mLabel;
- }
-
- public void setLabel(CharSequence mLabel) {
- this.mLabel = mLabel;
- }
-
- public Drawable getIcon() {
- return mIcon;
- }
-
- public void setIcon(Drawable mIcon) {
- this.mIcon = mIcon;
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
new file mode 100644
index 0000000..8d8c2f1
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
@@ -0,0 +1,440 @@
+/*
+ * 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.packageinstaller.v2.model
+
+import android.Manifest
+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.res.Resources
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.Build
+import android.os.Process
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import java.io.File
+
+object PackageUtil {
+ private val LOG_TAG = InstallRepository::class.java.simpleName
+ private const val DOWNLOADS_AUTHORITY = "downloads"
+ private const val SPLIT_BASE_APK_END_WITH = "base.apk"
+
+ /**
+ * Determines if the UID belongs to the system downloads provider and returns the
+ * [ApplicationInfo] of the provider
+ *
+ * @param uid UID of the caller
+ * @return [ApplicationInfo] of the provider if a downloads provider exists, it is a
+ * system app, and its UID matches with the passed UID, null otherwise.
+ */
+ private fun getSystemDownloadsProviderInfo(pm: PackageManager, uid: Int): ApplicationInfo? {
+ // Check if there are currently enabled downloads provider on the system.
+ val providerInfo = pm.resolveContentProvider(DOWNLOADS_AUTHORITY, 0)
+ ?: return null
+ val appInfo = providerInfo.applicationInfo
+ return if ((appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) && uid == appInfo.uid) {
+ appInfo
+ } else null
+ }
+
+ /**
+ * Get the maximum target sdk for a UID.
+ *
+ * @param context The context to use
+ * @param uid The UID requesting the install/uninstall
+ * @return The maximum target SDK or -1 if the uid does not match any packages.
+ */
+ @JvmStatic
+ fun getMaxTargetSdkVersionForUid(context: Context, uid: Int): Int {
+ val pm = context.packageManager
+ val packages = pm.getPackagesForUid(uid)
+ var targetSdkVersion = -1
+ if (packages != null) {
+ for (packageName in packages) {
+ try {
+ val info = pm.getApplicationInfo(packageName!!, 0)
+ targetSdkVersion = maxOf(targetSdkVersion, info.targetSdkVersion)
+ } catch (e: PackageManager.NameNotFoundException) {
+ // Ignore and try the next package
+ }
+ }
+ }
+ return targetSdkVersion
+ }
+
+ @JvmStatic
+ fun canPackageQuery(context: Context, callingUid: Int, packageUri: Uri): Boolean {
+ val pm = context.packageManager
+ val info = pm.resolveContentProvider(
+ packageUri.authority!!,
+ PackageManager.ComponentInfoFlags.of(0)
+ ) ?: return false
+ val targetPackage = info.packageName
+ val callingPackages = pm.getPackagesForUid(callingUid) ?: return false
+ for (callingPackage in callingPackages) {
+ try {
+ if (pm.canPackageQuery(callingPackage!!, targetPackage)) {
+ return true
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ // no-op
+ }
+ }
+ return false
+ }
+
+ /**
+ * @param context the [Context] object
+ * @param permission the permission name to check
+ * @param callingUid the UID of the caller who's permission is being checked
+ * @return `true` if the callingUid is granted the said permission
+ */
+ @JvmStatic
+ fun isPermissionGranted(context: Context, permission: String, callingUid: Int): Boolean {
+ return (context.checkPermission(permission, -1, callingUid)
+ == PackageManager.PERMISSION_GRANTED)
+ }
+
+ /**
+ * @param pm the [PackageManager] object
+ * @param permission the permission name to check
+ * @param packageName the name of the package who's permission is being checked
+ * @return `true` if the package is granted the said permission
+ */
+ @JvmStatic
+ fun isPermissionGranted(pm: PackageManager, permission: String, packageName: String): Boolean {
+ return pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED
+ }
+
+ /**
+ * @param context the [Context] object
+ * @param callingUid the UID of the caller who's permission is being checked
+ * @param originatingUid the UID from where install is being originated. This could be same as
+ * callingUid or it will be the UID of the package performing a session based install
+ * @param isTrustedSource whether install request is coming from a privileged app or an app that
+ * has [Manifest.permission.INSTALL_PACKAGES] permission granted
+ * @return `true` if the package is granted the said permission
+ */
+ @JvmStatic
+ fun isInstallPermissionGrantedOrRequested(
+ context: Context,
+ callingUid: Int,
+ originatingUid: Int,
+ isTrustedSource: Boolean,
+ ): Boolean {
+ val isDocumentsManager =
+ isPermissionGranted(context, Manifest.permission.MANAGE_DOCUMENTS, callingUid)
+ val isSystemDownloadsProvider =
+ getSystemDownloadsProviderInfo(context.packageManager, callingUid) != null
+
+ if (!isTrustedSource && !isSystemDownloadsProvider && !isDocumentsManager) {
+ val targetSdkVersion = getMaxTargetSdkVersionForUid(context, originatingUid)
+ if (targetSdkVersion < 0) {
+ // Invalid originating uid supplied. Abort install.
+ Log.w(LOG_TAG, "Cannot get target sdk version for uid $originatingUid")
+ return false
+ } else if (targetSdkVersion >= Build.VERSION_CODES.O
+ && !isUidRequestingPermission(
+ context.packageManager, originatingUid,
+ Manifest.permission.REQUEST_INSTALL_PACKAGES
+ )
+ ) {
+ Log.e(
+ LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ + Manifest.permission.REQUEST_INSTALL_PACKAGES
+ )
+ return false
+ }
+ }
+ return true
+ }
+
+ /**
+ * @param pm the [PackageManager] object
+ * @param uid the UID of the caller who's permission is being checked
+ * @param permission the permission name to check
+ * @return `true` if the caller is requesting the said permission in its Manifest
+ */
+ private fun isUidRequestingPermission(
+ pm: PackageManager,
+ uid: Int,
+ permission: String,
+ ): Boolean {
+ val packageNames = pm.getPackagesForUid(uid) ?: return false
+ for (packageName in packageNames) {
+ val packageInfo: PackageInfo = try {
+ pm.getPackageInfo(packageName!!, PackageManager.GET_PERMISSIONS)
+ } catch (e: PackageManager.NameNotFoundException) {
+ // Ignore and try the next package
+ continue
+ }
+ if (packageInfo.requestedPermissions != null
+ && listOf(*packageInfo.requestedPermissions!!).contains(permission)
+ ) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
+ * @param pi the [PackageInstaller] object to use
+ * @param originatingUid the UID of the package performing a session based install
+ * @param sessionId ID of the install session
+ * @return `true` if the caller is the session owner
+ */
+ @JvmStatic
+ fun isCallerSessionOwner(pi: PackageInstaller, originatingUid: Int, sessionId: Int): Boolean {
+ if (originatingUid == Process.ROOT_UID) {
+ return true
+ }
+ val sessionInfo = pi.getSessionInfo(sessionId) ?: return false
+ val installerUid = sessionInfo.getInstallerUid()
+ return originatingUid == installerUid
+ }
+
+ /**
+ * Generates a stub [PackageInfo] object for the given packageName
+ */
+ @JvmStatic
+ fun generateStubPackageInfo(packageName: String?): PackageInfo {
+ val info = PackageInfo()
+ val aInfo = ApplicationInfo()
+ info.applicationInfo = aInfo
+ info.applicationInfo!!.packageName = packageName
+ info.packageName = info.applicationInfo!!.packageName
+ return info
+ }
+
+ /**
+ * Generates an [AppSnippet] containing an appIcon and appLabel from the
+ * [PackageInstaller.SessionInfo] object
+ */
+ @JvmStatic
+ fun getAppSnippet(context: Context, info: PackageInstaller.SessionInfo): AppSnippet {
+ val pm = context.packageManager
+ val label = info.getAppLabel()
+ val icon = if (info.getAppIcon() != null) BitmapDrawable(
+ context.resources,
+ info.getAppIcon()
+ ) else pm.defaultActivityIcon
+ return AppSnippet(label, icon)
+ }
+
+ /**
+ * Generates an [AppSnippet] containing an appIcon and appLabel from the
+ * [PackageInfo] object
+ */
+ @JvmStatic
+ fun getAppSnippet(context: Context, pkgInfo: PackageInfo): AppSnippet {
+ return pkgInfo.applicationInfo?.let { getAppSnippet(context, it) } ?: run {
+ AppSnippet(pkgInfo.packageName, context.packageManager.defaultActivityIcon)
+ }
+ }
+
+ /**
+ * Generates an [AppSnippet] containing an appIcon and appLabel from the
+ * [ApplicationInfo] object
+ */
+ @JvmStatic
+ fun getAppSnippet(context: Context, appInfo: ApplicationInfo): AppSnippet {
+ val pm = context.packageManager
+ val label = pm.getApplicationLabel(appInfo)
+ val icon = pm.getApplicationIcon(appInfo)
+ return AppSnippet(label, icon)
+ }
+
+ /**
+ * Generates an [AppSnippet] containing an appIcon and appLabel from the
+ * supplied APK file
+ */
+ @JvmStatic
+ fun getAppSnippet(context: Context, pkgInfo: PackageInfo, sourceFile: File): AppSnippet {
+ pkgInfo.applicationInfo?.let {
+ val appInfoFromFile = processAppInfoForFile(it, sourceFile)
+ val label = getAppLabelFromFile(context, appInfoFromFile)
+ val icon = getAppIconFromFile(context, appInfoFromFile)
+ return AppSnippet(label, icon)
+ } ?: run {
+ return AppSnippet(pkgInfo.packageName, context.packageManager.defaultActivityIcon)
+ }
+ }
+
+ /**
+ * Utility method to load application label
+ *
+ * @param context context of package that can load the resources
+ * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+ */
+ private fun getAppLabelFromFile(context: Context, appInfo: ApplicationInfo): CharSequence? {
+ val pm = context.packageManager
+ var label: CharSequence? = null
+ // Try to load the label from the package's resources. If an app has not explicitly
+ // specified any label, just use the package name.
+ if (appInfo.labelRes != 0) {
+ try {
+ label = appInfo.loadLabel(pm)
+ } catch (e: Resources.NotFoundException) {
+ }
+ }
+ if (label == null) {
+ label = if (appInfo.nonLocalizedLabel != null) appInfo.nonLocalizedLabel
+ else appInfo.packageName
+ }
+ return label
+ }
+
+ /**
+ * Utility method to load application icon
+ *
+ * @param context context of package that can load the resources
+ * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+ */
+ private fun getAppIconFromFile(context: Context, appInfo: ApplicationInfo): Drawable? {
+ val pm = context.packageManager
+ var icon: Drawable? = null
+ // Try to load the icon from the package's resources. If an app has not explicitly
+ // specified any resource, just use the default icon for now.
+ try {
+ if (appInfo.icon != 0) {
+ try {
+ icon = appInfo.loadIcon(pm)
+ } catch (e: Resources.NotFoundException) {
+ }
+ }
+ if (icon == null) {
+ icon = context.packageManager.defaultActivityIcon
+ }
+ } catch (e: OutOfMemoryError) {
+ Log.i(LOG_TAG, "Could not load app icon", e)
+ }
+ return icon
+ }
+
+ private fun processAppInfoForFile(appInfo: ApplicationInfo, sourceFile: File): ApplicationInfo {
+ val archiveFilePath = sourceFile.absolutePath
+ appInfo.publicSourceDir = archiveFilePath
+ if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) {
+ val files = sourceFile.parentFile?.listFiles()
+ val splits = appInfo.splitNames!!
+ .mapNotNull { findFilePath(files, "$it.apk") }
+ .toTypedArray()
+
+ appInfo.splitSourceDirs = splits
+ appInfo.splitPublicSourceDirs = splits
+ }
+ return appInfo
+ }
+
+ private fun findFilePath(files: Array<File>?, postfix: String): String? {
+ files?.let {
+ for (file in it) {
+ val path = file.absolutePath
+ if (path.endsWith(postfix)) {
+ return path
+ }
+ }
+ }
+ return null
+ }
+
+ /**
+ * @return the packageName corresponding to a UID.
+ */
+ @JvmStatic
+ fun getPackageNameForUid(context: Context, sourceUid: Int, callingPackage: String?): String? {
+ if (sourceUid == Process.INVALID_UID) {
+ return null
+ }
+ // If the sourceUid belongs to the system downloads provider, we explicitly return the
+ // name of the Download Manager package. This is because its UID is shared with multiple
+ // packages, resulting in uncertainty about which package will end up first in the list
+ // of packages associated with this UID
+ val pm = context.packageManager
+ val systemDownloadProviderInfo = getSystemDownloadsProviderInfo(pm, sourceUid)
+ if (systemDownloadProviderInfo != null) {
+ return systemDownloadProviderInfo.packageName
+ }
+ val packagesForUid = pm.getPackagesForUid(sourceUid) ?: return null
+ if (packagesForUid.size > 1) {
+ if (callingPackage != null) {
+ for (packageName in packagesForUid) {
+ if (packageName == callingPackage) {
+ return packageName
+ }
+ }
+ }
+ Log.i(LOG_TAG, "Multiple packages found for source uid $sourceUid")
+ }
+ return packagesForUid[0]
+ }
+
+ /**
+ * Utility method to get package information for a given [File]
+ */
+ @JvmStatic
+ fun getPackageInfo(context: Context, sourceFile: File, flags: Int): PackageInfo? {
+ var filePath = sourceFile.absolutePath
+ if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) {
+ val dir = sourceFile.parentFile
+ if ((dir?.listFiles()?.size ?: 0) > 1) {
+ // split apks, use file directory to get archive info
+ filePath = dir.path
+ }
+ }
+ return try {
+ context.packageManager.getPackageArchiveInfo(filePath, flags)
+ } catch (ignored: Exception) {
+ null
+ }
+ }
+
+ /**
+ * Is a profile part of a user?
+ *
+ * @param userManager The user manager
+ * @param userHandle The handle of the user
+ * @param profileHandle The handle of the profile
+ *
+ * @return If the profile is part of the user or the profile parent of the user
+ */
+ @JvmStatic
+ fun isProfileOfOrSame(
+ userManager: UserManager,
+ userHandle: UserHandle,
+ profileHandle: UserHandle?,
+ ): Boolean {
+ if (profileHandle == null) {
+ return false
+ }
+ return if (userHandle == profileHandle) {
+ true
+ } else userManager.getProfileParent(profileHandle) != null
+ && userManager.getProfileParent(profileHandle) == userHandle
+ }
+
+ /**
+ * The class to hold an incoming package's icon and label.
+ * See [getAppSnippet]
+ */
+ data class AppSnippet(var label: CharSequence?, var icon: Drawable?)
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java
deleted file mode 100644
index a2c81f1..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.java
+++ /dev/null
@@ -1,126 +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.packageinstaller.v2.model;
-
-import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH;
-
-import android.content.Context;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.util.Log;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.v2.model.InstallRepository.SessionStageListener;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class SessionStager extends AsyncTask<Void, Integer, SessionInfo> {
-
- private static final String TAG = SessionStager.class.getSimpleName();
- private final Context mContext;
- private final Uri mUri;
- private final int mStagedSessionId;
- private final MutableLiveData<Integer> mProgressLiveData = new MutableLiveData<>(0);
- private final SessionStageListener mListener;
-
- SessionStager(Context context, Uri uri, int stagedSessionId, SessionStageListener listener) {
- mContext = context;
- mUri = uri;
- mStagedSessionId = stagedSessionId;
- mListener = listener;
- }
-
- @Override
- protected PackageInstaller.SessionInfo doInBackground(Void... params) {
- PackageInstaller pi = mContext.getPackageManager().getPackageInstaller();
- try (PackageInstaller.Session session = pi.openSession(mStagedSessionId);
- InputStream in = mContext.getContentResolver().openInputStream(mUri)) {
- session.setStagingProgress(0);
-
- if (in == null) {
- return null;
- }
- final long sizeBytes = getContentSizeBytes();
- mProgressLiveData.postValue(sizeBytes > 0 ? 0 : -1);
-
- long totalRead = 0;
- try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {
- byte[] buffer = new byte[1024 * 1024];
- while (true) {
- int numRead = in.read(buffer);
-
- if (numRead == -1) {
- session.fsync(out);
- break;
- }
-
- if (isCancelled()) {
- break;
- }
-
- out.write(buffer, 0, numRead);
- if (sizeBytes > 0) {
- totalRead += numRead;
- float fraction = ((float) totalRead / (float) sizeBytes);
- session.setStagingProgress(fraction);
- publishProgress((int) (fraction * 100.0));
- }
- }
- }
- return pi.getSessionInfo(mStagedSessionId);
- } catch (IOException | SecurityException | IllegalStateException
- | IllegalArgumentException e) {
- Log.w(TAG, "Error staging apk from content URI", e);
- return null;
- }
- }
-
- private long getContentSizeBytes() {
- try (AssetFileDescriptor afd = mContext.getContentResolver()
- .openAssetFileDescriptor(mUri, "r")) {
- return afd != null ? afd.getLength() : UNKNOWN_LENGTH;
- } catch (IOException e) {
- Log.w(TAG, "Failed to open asset file descriptor", e);
- return UNKNOWN_LENGTH;
- }
- }
-
- public MutableLiveData<Integer> getProgress() {
- return mProgressLiveData;
- }
-
- @Override
- protected void onProgressUpdate(Integer... progress) {
- if (progress != null && progress.length > 0) {
- mProgressLiveData.setValue(progress[0]);
- }
- }
-
- @Override
- protected void onPostExecute(SessionInfo sessionInfo) {
- if (sessionInfo == null || !sessionInfo.isActive()
- || sessionInfo.getResolvedBaseApkPath() == null) {
- Log.w(TAG, "Session info is invalid: " + sessionInfo);
- mListener.onStagingFailure();
- return;
- }
- mListener.onStagingSuccess(sessionInfo);
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt
new file mode 100644
index 0000000..c9bfa17
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.packageinstaller.v2.model
+
+import android.content.Context
+import android.content.pm.PackageInstaller
+import android.content.res.AssetFileDescriptor
+import android.net.Uri
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import java.io.IOException
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class SessionStager internal constructor(
+ private val context: Context,
+ private val uri: Uri,
+ private val stagedSessionId: Int
+) {
+
+ companion object {
+ private val LOG_TAG = SessionStager::class.java.simpleName
+ }
+
+ private val _progress = MutableLiveData(0)
+ val progress: LiveData<Int>
+ get() = _progress
+
+ suspend fun execute(): Boolean = withContext(Dispatchers.IO) {
+ val pi: PackageInstaller = context.packageManager.packageInstaller
+ var sessionInfo: PackageInstaller.SessionInfo?
+ try {
+ val session = pi.openSession(stagedSessionId)
+ context.contentResolver.openInputStream(uri).use { instream ->
+ session.setStagingProgress(0f)
+
+ if (instream == null) {
+ return@withContext false
+ }
+
+ val sizeBytes = getContentSizeBytes()
+ publishProgress(if (sizeBytes > 0) 0 else -1)
+
+ var totalRead: Long = 0
+ session.openWrite("PackageInstaller", 0, sizeBytes).use { out ->
+ val buffer = ByteArray(1024 * 1024)
+ while (true) {
+ val numRead = instream.read(buffer)
+ if (numRead == -1) {
+ session.fsync(out)
+ break
+ }
+ out.write(buffer, 0, numRead)
+
+ if (sizeBytes > 0) {
+ totalRead += numRead.toLong()
+ val fraction = totalRead.toFloat() / sizeBytes.toFloat()
+ session.setStagingProgress(fraction)
+ publishProgress((fraction * 100.0).toInt())
+ }
+ }
+ }
+ sessionInfo = pi.getSessionInfo(stagedSessionId)
+ }
+ } catch (e: Exception) {
+ Log.w(LOG_TAG, "Error staging apk from content URI", e)
+ sessionInfo = null
+ }
+
+ return@withContext if (sessionInfo == null
+ || !sessionInfo?.isActive!!
+ || sessionInfo?.resolvedBaseApkPath == null
+ ) {
+ Log.w(LOG_TAG, "Session info is invalid: $sessionInfo")
+ false
+ } else {
+ true
+ }
+ }
+
+ private fun getContentSizeBytes(): Long {
+ return try {
+ context.contentResolver
+ .openAssetFileDescriptor(uri, "r")
+ .use { afd -> afd?.length ?: AssetFileDescriptor.UNKNOWN_LENGTH }
+ } catch (e: IOException) {
+ Log.w(LOG_TAG, "Failed to open asset file descriptor", e)
+ AssetFileDescriptor.UNKNOWN_LENGTH
+ }
+ }
+
+ private suspend fun publishProgress(progressValue: Int) = withContext(Dispatchers.Main) {
+ _progress.value = progressValue
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
deleted file mode 100644
index a07c532..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
+++ /dev/null
@@ -1,716 +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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
-import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
-import static com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionForUid;
-import static com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid;
-import static com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted;
-import static com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame;
-import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_APP_UNAVAILABLE;
-import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_GENERIC_ERROR;
-import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.app.usage.StorageStats;
-import android.app.usage.StorageStatsManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-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.UninstallCompleteCallback;
-import android.content.pm.VersionedPackage;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.common.EventResultPersister;
-import com.android.packageinstaller.common.UninstallEventReceiver;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallFailed;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallReady;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallSuccess;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
-import java.io.IOException;
-import java.util.List;
-
-public class UninstallRepository {
-
- private static final String TAG = UninstallRepository.class.getSimpleName();
- private static final String UNINSTALL_FAILURE_CHANNEL = "uninstall_failure";
- private static final String BROADCAST_ACTION =
- "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
-
- private static final String EXTRA_UNINSTALL_ID =
- "com.android.packageinstaller.extra.UNINSTALL_ID";
- private static final String EXTRA_APP_LABEL =
- "com.android.packageinstaller.extra.APP_LABEL";
- private static final String EXTRA_IS_CLONE_APP =
- "com.android.packageinstaller.extra.IS_CLONE_APP";
- private static final String EXTRA_PACKAGE_NAME =
- "com.android.packageinstaller.extra.EXTRA_PACKAGE_NAME";
-
- private final Context mContext;
- private final AppOpsManager mAppOpsManager;
- private final PackageManager mPackageManager;
- private final UserManager mUserManager;
- private final NotificationManager mNotificationManager;
- private final MutableLiveData<UninstallStage> mUninstallResult = new MutableLiveData<>();
- public UserHandle mUninstalledUser;
- public UninstallCompleteCallback mCallback;
- private ApplicationInfo mTargetAppInfo;
- private ActivityInfo mTargetActivityInfo;
- private Intent mIntent;
- private CharSequence mTargetAppLabel;
- private String mTargetPackageName;
- private String mCallingActivity;
- private boolean mUninstallFromAllUsers;
- private boolean mIsClonedApp;
- private int mUninstallId;
-
- public UninstallRepository(Context context) {
- mContext = context;
- mAppOpsManager = context.getSystemService(AppOpsManager.class);
- mPackageManager = context.getPackageManager();
- mUserManager = context.getSystemService(UserManager.class);
- mNotificationManager = context.getSystemService(NotificationManager.class);
- }
-
- public UninstallStage performPreUninstallChecks(Intent intent, CallerInfo callerInfo) {
- mIntent = intent;
-
- int callingUid = callerInfo.getUid();
- mCallingActivity = callerInfo.getActivityName();
-
- if (callingUid == Process.INVALID_UID) {
- Log.e(TAG, "Could not determine the launching uid.");
- return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
- // TODO: should we give any indication to the user?
- }
-
- String callingPackage = getPackageNameForUid(mContext, callingUid, null);
- if (callingPackage == null) {
- Log.e(TAG, "Package not found for originating uid " + callingUid);
- return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
- } else {
- if (mAppOpsManager.noteOpNoThrow(
- AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage)
- != MODE_ALLOWED) {
- Log.e(TAG, "Install from uid " + callingUid + " disallowed by AppOps");
- return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
- }
- }
-
- if (getMaxTargetSdkVersionForUid(mContext, callingUid) >= Build.VERSION_CODES.P
- && !isPermissionGranted(mContext, Manifest.permission.REQUEST_DELETE_PACKAGES,
- callingUid)
- && !isPermissionGranted(mContext, Manifest.permission.DELETE_PACKAGES, callingUid)) {
- Log.e(TAG, "Uid " + callingUid + " does not have "
- + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
- + Manifest.permission.DELETE_PACKAGES);
-
- return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
- }
-
- // Get intent information.
- // We expect an intent with URI of the form package:<packageName>#<className>
- // className is optional; if specified, it is the activity the user chose to uninstall
- final Uri packageUri = intent.getData();
- if (packageUri == null) {
- Log.e(TAG, "No package URI in intent");
- return new UninstallAborted(ABORT_REASON_APP_UNAVAILABLE);
- }
- mTargetPackageName = packageUri.getEncodedSchemeSpecificPart();
- if (mTargetPackageName == null) {
- Log.e(TAG, "Invalid package name in URI: " + packageUri);
- return new UninstallAborted(ABORT_REASON_APP_UNAVAILABLE);
- }
-
- mUninstallFromAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS,
- false);
- if (mUninstallFromAllUsers && !mUserManager.isAdminUser()) {
- Log.e(TAG, "Only admin user can request uninstall for all users");
- return new UninstallAborted(ABORT_REASON_USER_NOT_ALLOWED);
- }
-
- mUninstalledUser = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
- if (mUninstalledUser == null) {
- mUninstalledUser = Process.myUserHandle();
- } else {
- List<UserHandle> profiles = mUserManager.getUserProfiles();
- if (!profiles.contains(mUninstalledUser)) {
- Log.e(TAG, "User " + Process.myUserHandle() + " can't request uninstall "
- + "for user " + mUninstalledUser);
- return new UninstallAborted(ABORT_REASON_USER_NOT_ALLOWED);
- }
- }
-
- mCallback = intent.getParcelableExtra(PackageInstaller.EXTRA_CALLBACK,
- PackageManager.UninstallCompleteCallback.class);
-
- try {
- mTargetAppInfo = mPackageManager.getApplicationInfo(mTargetPackageName,
- PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER));
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Unable to get packageName");
- }
-
- if (mTargetAppInfo == null) {
- Log.e(TAG, "Invalid packageName: " + mTargetPackageName);
- return new UninstallAborted(ABORT_REASON_APP_UNAVAILABLE);
- }
-
- // The class name may have been specified (e.g. when deleting an app from all apps)
- final String className = packageUri.getFragment();
- if (className != null) {
- try {
- mTargetActivityInfo = mPackageManager.getActivityInfo(
- new ComponentName(mTargetPackageName, className),
- PackageManager.ComponentInfoFlags.of(0));
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Unable to get className");
- // Continue as the ActivityInfo isn't critical.
- }
- }
-
- return new UninstallReady();
- }
-
- public UninstallStage generateUninstallDetails() {
- UninstallUserActionRequired.Builder uarBuilder = new UninstallUserActionRequired.Builder();
- StringBuilder messageBuilder = new StringBuilder();
-
- mTargetAppLabel = mTargetAppInfo.loadSafeLabel(mPackageManager);
-
- // If the Activity label differs from the App label, then make sure the user
- // knows the Activity belongs to the App being uninstalled.
- if (mTargetActivityInfo != null) {
- final CharSequence activityLabel = mTargetActivityInfo.loadSafeLabel(mPackageManager);
- if (CharSequence.compare(activityLabel, mTargetAppLabel) != 0) {
- messageBuilder.append(
- mContext.getString(R.string.uninstall_activity_text, activityLabel));
- messageBuilder.append(" ").append(mTargetAppLabel).append(".\n\n");
- }
- }
-
- final boolean isUpdate =
- (mTargetAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
- final UserHandle myUserHandle = Process.myUserHandle();
- boolean isSingleUser = isSingleUser();
-
- if (isUpdate) {
- messageBuilder.append(mContext.getString(
- isSingleUser ? R.string.uninstall_update_text :
- R.string.uninstall_update_text_multiuser));
- } else if (mUninstallFromAllUsers && !isSingleUser) {
- messageBuilder.append(mContext.getString(
- R.string.uninstall_application_text_all_users));
- } else if (!mUninstalledUser.equals(myUserHandle)) {
- // Uninstalling user is issuing uninstall for another user
- UserManager customUserManager = mContext.createContextAsUser(mUninstalledUser, 0)
- .getSystemService(UserManager.class);
- String userName = customUserManager.getUserName();
-
- String uninstalledUserType = getUninstalledUserType(myUserHandle, mUninstalledUser);
- String messageString;
- if (USER_TYPE_PROFILE_MANAGED.equals(uninstalledUserType)) {
- messageString = mContext.getString(
- R.string.uninstall_application_text_current_user_work_profile, userName);
- } else if (USER_TYPE_PROFILE_CLONE.equals(uninstalledUserType)) {
- mIsClonedApp = true;
- messageString = mContext.getString(
- R.string.uninstall_application_text_current_user_clone_profile);
- } else {
- messageString = mContext.getString(
- R.string.uninstall_application_text_user, userName);
- }
- messageBuilder.append(messageString);
- } else if (isCloneProfile(mUninstalledUser)) {
- mIsClonedApp = true;
- messageBuilder.append(mContext.getString(
- R.string.uninstall_application_text_current_user_clone_profile));
- } else if (myUserHandle.equals(UserHandle.SYSTEM)
- && hasClonedInstance(mTargetAppInfo.packageName)) {
- messageBuilder.append(mContext.getString(
- R.string.uninstall_application_text_with_clone_instance, mTargetAppLabel));
- } else {
- messageBuilder.append(mContext.getString(R.string.uninstall_application_text));
- }
-
- uarBuilder.setMessage(messageBuilder.toString());
-
- if (mIsClonedApp) {
- uarBuilder.setTitle(mContext.getString(R.string.cloned_app_label, mTargetAppLabel));
- } else {
- uarBuilder.setTitle(mTargetAppLabel.toString());
- }
-
- boolean suggestToKeepAppData = false;
- try {
- PackageInfo pkgInfo = mPackageManager.getPackageInfo(mTargetPackageName, 0);
- suggestToKeepAppData =
- pkgInfo.applicationInfo != null && pkgInfo.applicationInfo.hasFragileUserData();
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Cannot check hasFragileUserData for " + mTargetPackageName, e);
- }
-
- long appDataSize = 0;
- if (suggestToKeepAppData) {
- appDataSize = getAppDataSize(mTargetPackageName,
- mUninstallFromAllUsers ? null : mUninstalledUser);
- }
- uarBuilder.setAppDataSize(appDataSize);
-
- return uarBuilder.build();
- }
-
- /**
- * Returns whether there is only one "full" user on this device.
- *
- * <p><b>Note:</b> on devices that use {@link android.os.UserManager#isHeadlessSystemUserMode()
- * headless system user mode}, the system user is not "full", so it's not be considered in the
- * calculation.</p>
- */
- private boolean isSingleUser() {
- final int userCount = mUserManager.getUserCount();
- return userCount == 1 || (UserManager.isHeadlessSystemUserMode() && userCount == 2);
- }
-
- /**
- * Returns the type of the user from where an app is being uninstalled. We are concerned with
- * only USER_TYPE_PROFILE_MANAGED and USER_TYPE_PROFILE_CLONE and whether the user and profile
- * belong to the same profile group.
- */
- @Nullable
- private String getUninstalledUserType(UserHandle myUserHandle,
- UserHandle uninstalledUserHandle) {
- if (!mUserManager.isSameProfileGroup(myUserHandle, uninstalledUserHandle)) {
- return null;
- }
-
- UserManager customUserManager = mContext.createContextAsUser(uninstalledUserHandle, 0)
- .getSystemService(UserManager.class);
- String[] userTypes = {USER_TYPE_PROFILE_MANAGED, USER_TYPE_PROFILE_CLONE};
- for (String userType : userTypes) {
- if (customUserManager.isUserOfType(userType)) {
- return userType;
- }
- }
- return null;
- }
-
- private boolean hasClonedInstance(String packageName) {
- // Check if clone user is present on the device.
- UserHandle cloneUser = null;
- List<UserHandle> profiles = mUserManager.getUserProfiles();
- for (UserHandle userHandle : profiles) {
- if (!userHandle.equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
- cloneUser = userHandle;
- break;
- }
- }
- // Check if another instance of given package exists in clone user profile.
- try {
- return cloneUser != null
- && mPackageManager.getPackageUidAsUser(packageName,
- PackageManager.PackageInfoFlags.of(0), cloneUser.getIdentifier()) > 0;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
- }
-
- private boolean isCloneProfile(UserHandle userHandle) {
- UserManager customUserManager = mContext.createContextAsUser(userHandle, 0)
- .getSystemService(UserManager.class);
- return customUserManager.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE);
- }
-
- /**
- * Get number of bytes of the app data of the package.
- *
- * @param pkg The package that might have app data.
- * @param user The user the package belongs to or {@code null} if files of all users should
- * be counted.
- * @return The number of bytes.
- */
- private long getAppDataSize(@NonNull String pkg, @Nullable UserHandle user) {
- if (user != null) {
- return getAppDataSizeForUser(pkg, user);
- }
- // We are uninstalling from all users. Get cumulative app data size for all users.
- List<UserHandle> userHandles = mUserManager.getUserHandles(true);
- long totalAppDataSize = 0;
- int numUsers = userHandles.size();
- for (int i = 0; i < numUsers; i++) {
- totalAppDataSize += getAppDataSizeForUser(pkg, userHandles.get(i));
- }
- return totalAppDataSize;
- }
-
- /**
- * Get number of bytes of the app data of the package.
- *
- * @param pkg The package that might have app data.
- * @param user The user the package belongs to
- * @return The number of bytes.
- */
- private long getAppDataSizeForUser(@NonNull String pkg, @NonNull UserHandle user) {
- StorageStatsManager storageStatsManager =
- mContext.getSystemService(StorageStatsManager.class);
- try {
- StorageStats stats = storageStatsManager.queryStatsForPackage(
- mPackageManager.getApplicationInfo(pkg, 0).storageUuid, pkg, user);
- return stats.getDataBytes();
- } catch (PackageManager.NameNotFoundException | IOException | SecurityException e) {
- Log.e(TAG, "Cannot determine amount of app data for " + pkg, e);
- }
- return 0;
- }
-
- public void initiateUninstall(boolean keepData) {
- // Get an uninstallId to track results and show a notification on non-TV devices.
- try {
- mUninstallId = UninstallEventReceiver.addObserver(mContext,
- EventResultPersister.GENERATE_NEW_ID, this::handleUninstallResult);
- } catch (EventResultPersister.OutOfIdsException e) {
- Log.e(TAG, "Failed to start uninstall", e);
- handleUninstallResult(PackageInstaller.STATUS_FAILURE,
- PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0);
- return;
- }
-
- // TODO: Check with UX whether to show UninstallUninstalling dialog / notification?
- mUninstallResult.setValue(new UninstallUninstalling(mTargetAppLabel, mIsClonedApp));
-
- Bundle uninstallData = new Bundle();
- uninstallData.putInt(EXTRA_UNINSTALL_ID, mUninstallId);
- uninstallData.putString(EXTRA_PACKAGE_NAME, mTargetPackageName);
- uninstallData.putBoolean(Intent.EXTRA_UNINSTALL_ALL_USERS, mUninstallFromAllUsers);
- uninstallData.putCharSequence(EXTRA_APP_LABEL, mTargetAppLabel);
- uninstallData.putBoolean(EXTRA_IS_CLONE_APP, mIsClonedApp);
- Log.i(TAG, "Uninstalling extras = " + uninstallData);
-
- // Get a PendingIntent for result broadcast and issue an uninstall request
- Intent broadcastIntent = new Intent(BROADCAST_ACTION);
- broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mUninstallId);
- broadcastIntent.setPackage(mContext.getPackageName());
-
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(mContext, mUninstallId, broadcastIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
-
- if (!startUninstall(mTargetPackageName, mUninstalledUser, pendingIntent,
- mUninstallFromAllUsers, keepData)) {
- handleUninstallResult(PackageInstaller.STATUS_FAILURE,
- PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0);
- }
- }
-
- private void handleUninstallResult(int status, int legacyStatus, @Nullable String message,
- int serviceId) {
- if (mCallback != null) {
- // The caller will be informed about the result via a callback
- mCallback.onUninstallComplete(mTargetPackageName, legacyStatus, message);
-
- // Since the caller already received the results, just finish the app at this point
- mUninstallResult.setValue(null);
- return;
- }
-
- boolean returnResult = mIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
- if (returnResult || mCallingActivity != null) {
- Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
-
- if (status == PackageInstaller.STATUS_SUCCESS) {
- UninstallSuccess.Builder successBuilder = new UninstallSuccess.Builder()
- .setResultIntent(intent)
- .setActivityResultCode(Activity.RESULT_OK);
- mUninstallResult.setValue(successBuilder.build());
- } else {
- UninstallFailed.Builder failedBuilder = new UninstallFailed.Builder(true)
- .setResultIntent(intent)
- .setActivityResultCode(Activity.RESULT_FIRST_USER);
- mUninstallResult.setValue(failedBuilder.build());
- }
- return;
- }
-
- // Caller did not want the result back. So, we either show a Toast, or a Notification.
- if (status == PackageInstaller.STATUS_SUCCESS) {
- UninstallSuccess.Builder successBuilder = new UninstallSuccess.Builder()
- .setActivityResultCode(legacyStatus)
- .setMessage(mIsClonedApp
- ? mContext.getString(R.string.uninstall_done_clone_app, mTargetAppLabel)
- : mContext.getString(R.string.uninstall_done_app, mTargetAppLabel));
- mUninstallResult.setValue(successBuilder.build());
- } else {
- UninstallFailed.Builder failedBuilder = new UninstallFailed.Builder(false);
- Notification.Builder uninstallFailedNotification = null;
-
- NotificationChannel uninstallFailureChannel = new NotificationChannel(
- UNINSTALL_FAILURE_CHANNEL,
- mContext.getString(R.string.uninstall_failure_notification_channel),
- NotificationManager.IMPORTANCE_DEFAULT);
- mNotificationManager.createNotificationChannel(uninstallFailureChannel);
-
- uninstallFailedNotification = new Notification.Builder(mContext,
- UNINSTALL_FAILURE_CHANNEL);
-
- UserHandle myUserHandle = Process.myUserHandle();
- switch (legacyStatus) {
- case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER -> {
- // Find out if the package is an active admin for some non-current user.
- UserHandle otherBlockingUserHandle =
- findUserOfDeviceAdmin(myUserHandle, mTargetPackageName);
-
- if (otherBlockingUserHandle == null) {
- Log.d(TAG, "Uninstall failed because " + mTargetPackageName
- + " is a device admin");
-
- addDeviceManagerButton(mContext, uninstallFailedNotification);
- setBigText(uninstallFailedNotification, mContext.getString(
- R.string.uninstall_failed_device_policy_manager));
- } else {
- Log.d(TAG, "Uninstall failed because " + mTargetPackageName
- + " is a device admin of user " + otherBlockingUserHandle);
-
- String userName =
- mContext.createContextAsUser(otherBlockingUserHandle, 0)
- .getSystemService(UserManager.class).getUserName();
- setBigText(uninstallFailedNotification, String.format(
- mContext.getString(
- R.string.uninstall_failed_device_policy_manager_of_user),
- userName));
- }
- }
- case PackageManager.DELETE_FAILED_OWNER_BLOCKED -> {
- UserHandle otherBlockingUserHandle = findBlockingUser(mTargetPackageName);
- boolean isProfileOfOrSame = isProfileOfOrSame(mUserManager, myUserHandle,
- otherBlockingUserHandle);
-
- if (isProfileOfOrSame) {
- addDeviceManagerButton(mContext, uninstallFailedNotification);
- } else {
- addManageUsersButton(mContext, uninstallFailedNotification);
- }
-
- String bigText = null;
- if (otherBlockingUserHandle == null) {
- Log.d(TAG, "Uninstall failed for " + mTargetPackageName +
- " with code " + status + " no blocking user");
- } else if (otherBlockingUserHandle == UserHandle.SYSTEM) {
- bigText = mContext.getString(
- R.string.uninstall_blocked_device_owner);
- } else {
- bigText = mContext.getString(mUninstallFromAllUsers ?
- R.string.uninstall_all_blocked_profile_owner
- : R.string.uninstall_blocked_profile_owner);
- }
- if (bigText != null) {
- setBigText(uninstallFailedNotification, bigText);
- }
- }
- default -> {
- Log.d(TAG, "Uninstall blocked for " + mTargetPackageName
- + " with legacy code " + legacyStatus);
- }
- }
-
- uninstallFailedNotification.setContentTitle(
- mContext.getString(R.string.uninstall_failed_app, mTargetAppLabel));
- uninstallFailedNotification.setOngoing(false);
- uninstallFailedNotification.setSmallIcon(R.drawable.ic_error);
- failedBuilder.setUninstallNotification(mUninstallId,
- uninstallFailedNotification.build());
-
- mUninstallResult.setValue(failedBuilder.build());
- }
- }
-
- /**
- * @param myUserHandle {@link UserHandle} of the current user.
- * @param packageName Name of the package being uninstalled.
- * @return the {@link UserHandle} of the user in which a package is a device admin.
- */
- @Nullable
- private UserHandle findUserOfDeviceAdmin(UserHandle myUserHandle, String packageName) {
- for (UserHandle otherUserHandle : mUserManager.getUserHandles(true)) {
- // We only catch the case when the user in question is neither the
- // current user nor its profile.
- if (isProfileOfOrSame(mUserManager, myUserHandle, otherUserHandle)) {
- continue;
- }
- DevicePolicyManager dpm = mContext.createContextAsUser(otherUserHandle, 0)
- .getSystemService(DevicePolicyManager.class);
- if (dpm.packageHasActiveAdmins(packageName)) {
- return otherUserHandle;
- }
- }
- return null;
- }
-
- /**
- *
- * @param packageName Name of the package being uninstalled.
- * @return {@link UserHandle} of the user in which a package is blocked from being uninstalled.
- */
- @Nullable
- private UserHandle findBlockingUser(String packageName) {
- for (UserHandle otherUserHandle : mUserManager.getUserHandles(true)) {
- // TODO (b/307399586): Add a negation when the logic of the method
- // is fixed
- if (mPackageManager.canUserUninstall(packageName, otherUserHandle)) {
- return otherUserHandle;
- }
- }
- return null;
- }
-
- /**
- * Set big text for the notification.
- *
- * @param builder The builder of the notification
- * @param text The text to set.
- */
- private void setBigText(@NonNull Notification.Builder builder,
- @NonNull CharSequence text) {
- builder.setStyle(new Notification.BigTextStyle().bigText(text));
- }
-
- /**
- * Add a button to the notification that links to the user management.
- *
- * @param context The context the notification is created in
- * @param builder The builder of the notification
- */
- private void addManageUsersButton(@NonNull Context context,
- @NonNull Notification.Builder builder) {
- builder.addAction((new Notification.Action.Builder(
- Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
- context.getString(R.string.manage_users),
- PendingIntent.getActivity(context, 0, getUserSettingsIntent(),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))).build());
- }
-
- private Intent getUserSettingsIntent() {
- Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-
- /**
- * Add a button to the notification that links to the device policy management.
- *
- * @param context The context the notification is created in
- * @param builder The builder of the notification
- */
- private void addDeviceManagerButton(@NonNull Context context,
- @NonNull Notification.Builder builder) {
- builder.addAction((new Notification.Action.Builder(
- Icon.createWithResource(context, R.drawable.ic_lock),
- context.getString(R.string.manage_device_administrators),
- PendingIntent.getActivity(context, 0, getDeviceManagerIntent(),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))).build());
- }
-
- private Intent getDeviceManagerIntent() {
- Intent intent = new Intent();
- intent.setClassName("com.android.settings",
- "com.android.settings.Settings$DeviceAdminSettingsActivity");
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-
- /**
- * Starts an uninstall for the given package.
- *
- * @return {@code true} if there was no exception while uninstalling. This does not represent
- * the result of the uninstall. Result will be made available in
- * {@link #handleUninstallResult(int, int, String, int)}
- */
- private boolean startUninstall(String packageName, UserHandle targetUser,
- PendingIntent pendingIntent, boolean uninstallFromAllUsers, boolean keepData) {
- int flags = uninstallFromAllUsers ? PackageManager.DELETE_ALL_USERS : 0;
- flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
- try {
- mContext.createContextAsUser(targetUser, 0)
- .getPackageManager().getPackageInstaller().uninstall(
- new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
- flags, pendingIntent.getIntentSender());
- return true;
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Failed to uninstall", e);
- return false;
- }
- }
-
- public void cancelInstall() {
- if (mCallback != null) {
- mCallback.onUninstallComplete(mTargetPackageName,
- PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
- }
- }
-
- public MutableLiveData<UninstallStage> getUninstallResult() {
- return mUninstallResult;
- }
-
- public static class CallerInfo {
-
- private final String mActivityName;
- private final int mUid;
-
- public CallerInfo(String activityName, int uid) {
- mActivityName = activityName;
- mUid = uid;
- }
-
- public String getActivityName() {
- return mActivityName;
- }
-
- public int getUid() {
- return mUid;
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
new file mode 100644
index 0000000..7cc95c5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -0,0 +1,739 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.packageinstaller.v2.model
+
+import android.Manifest
+import android.app.Activity
+import android.app.AppOpsManager
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.admin.DevicePolicyManager
+import android.app.usage.StorageStatsManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageManager
+import android.content.pm.VersionedPackage
+import android.graphics.drawable.Icon
+import android.os.Build
+import android.os.Bundle
+import android.os.Process
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.Settings
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.R
+import com.android.packageinstaller.common.EventResultPersister
+import com.android.packageinstaller.common.EventResultPersister.OutOfIdsException
+import com.android.packageinstaller.common.UninstallEventReceiver
+import com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionForUid
+import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
+import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
+import com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame
+
+class UninstallRepository(private val context: Context) {
+
+ private val appOpsManager: AppOpsManager? = context.getSystemService(AppOpsManager::class.java)
+ private val packageManager: PackageManager = context.packageManager
+ private val userManager: UserManager? = context.getSystemService(UserManager::class.java)
+ private val notificationManager: NotificationManager? =
+ context.getSystemService(NotificationManager::class.java)
+ val uninstallResult = MutableLiveData<UninstallStage?>()
+ private var uninstalledUser: UserHandle? = null
+ private var callback: PackageManager.UninstallCompleteCallback? = null
+ private var targetAppInfo: ApplicationInfo? = null
+ private var targetActivityInfo: ActivityInfo? = null
+ private lateinit var intent: Intent
+ private lateinit var targetAppLabel: CharSequence
+ private var targetPackageName: String? = null
+ private var callingActivity: String? = null
+ private var uninstallFromAllUsers = false
+ private var isClonedApp = false
+ private var uninstallId = 0
+
+ fun performPreUninstallChecks(intent: Intent, callerInfo: CallerInfo): UninstallStage {
+ this.intent = intent
+
+ val callingUid = callerInfo.uid
+ callingActivity = callerInfo.activityName
+
+ if (callingUid == Process.INVALID_UID) {
+ Log.e(LOG_TAG, "Could not determine the launching uid.")
+ return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+ // TODO: should we give any indication to the user?
+ }
+
+ val callingPackage = getPackageNameForUid(context, callingUid, null)
+ if (callingPackage == null) {
+ Log.e(LOG_TAG, "Package not found for originating uid $callingUid")
+ return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+ } else {
+ if (appOpsManager!!.noteOpNoThrow(
+ AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage
+ ) != AppOpsManager.MODE_ALLOWED
+ ) {
+ Log.e(LOG_TAG, "Install from uid $callingUid disallowed by AppOps")
+ return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+ }
+ }
+
+ if (getMaxTargetSdkVersionForUid(context, callingUid) >= Build.VERSION_CODES.P
+ && !isPermissionGranted(
+ context, Manifest.permission.REQUEST_DELETE_PACKAGES, callingUid
+ )
+ && !isPermissionGranted(context, Manifest.permission.DELETE_PACKAGES, callingUid)
+ ) {
+ Log.e(
+ LOG_TAG, "Uid " + callingUid + " does not have "
+ + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
+ + Manifest.permission.DELETE_PACKAGES
+ )
+ return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
+ }
+
+ // Get intent information.
+ // We expect an intent with URI of the form package:<packageName>#<className>
+ // className is optional; if specified, it is the activity the user chose to uninstall
+ val packageUri = intent.data
+ if (packageUri == null) {
+ Log.e(LOG_TAG, "No package URI in intent")
+ return UninstallAborted(UninstallAborted.ABORT_REASON_APP_UNAVAILABLE)
+ }
+ targetPackageName = packageUri.encodedSchemeSpecificPart
+ if (targetPackageName == null) {
+ Log.e(LOG_TAG, "Invalid package name in URI: $packageUri")
+ return UninstallAborted(UninstallAborted.ABORT_REASON_APP_UNAVAILABLE)
+ }
+
+ uninstallFromAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false)
+ if (uninstallFromAllUsers && !userManager!!.isAdminUser) {
+ Log.e(LOG_TAG, "Only admin user can request uninstall for all users")
+ return UninstallAborted(UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED)
+ }
+
+ uninstalledUser = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle::class.java)
+ if (uninstalledUser == null) {
+ uninstalledUser = Process.myUserHandle()
+ } else {
+ val profiles = userManager!!.userProfiles
+ if (!profiles.contains(uninstalledUser)) {
+ Log.e(
+ LOG_TAG, "User " + Process.myUserHandle() + " can't request uninstall "
+ + "for user " + uninstalledUser
+ )
+ return UninstallAborted(UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED)
+ }
+ }
+
+ callback = intent.getParcelableExtra(
+ PackageInstaller.EXTRA_CALLBACK, PackageManager.UninstallCompleteCallback::class.java
+ )
+
+ try {
+ targetAppInfo = packageManager.getApplicationInfo(
+ targetPackageName!!,
+ PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER.toLong())
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(LOG_TAG, "Unable to get packageName")
+ }
+
+ if (targetAppInfo == null) {
+ Log.e(LOG_TAG, "Invalid packageName: $targetPackageName")
+ return UninstallAborted(UninstallAborted.ABORT_REASON_APP_UNAVAILABLE)
+ }
+
+ // The class name may have been specified (e.g. when deleting an app from all apps)
+ val className = packageUri.fragment
+ if (className != null) {
+ try {
+ targetActivityInfo = packageManager.getActivityInfo(
+ ComponentName(targetPackageName!!, className),
+ PackageManager.ComponentInfoFlags.of(0)
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(LOG_TAG, "Unable to get className")
+ // Continue as the ActivityInfo isn't critical.
+ }
+ }
+
+ return UninstallReady()
+ }
+
+ fun generateUninstallDetails(): UninstallStage {
+ val messageBuilder = StringBuilder()
+
+ targetAppLabel = targetAppInfo!!.loadSafeLabel(packageManager)
+
+ // If the Activity label differs from the App label, then make sure the user
+ // knows the Activity belongs to the App being uninstalled.
+ if (targetActivityInfo != null) {
+ val activityLabel = targetActivityInfo!!.loadSafeLabel(packageManager)
+ if (!activityLabel.contentEquals(targetAppLabel)) {
+ messageBuilder.append(
+ context.getString(R.string.uninstall_activity_text, activityLabel)
+ )
+ messageBuilder.append(" ").append(targetAppLabel).append(".\n\n")
+ }
+ }
+
+ val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+ val myUserHandle = Process.myUserHandle()
+ val isSingleUser = isSingleUser()
+
+ if (isUpdate) {
+ messageBuilder.append(context.getString(
+ if (isSingleUser) R.string.uninstall_update_text
+ else R.string.uninstall_update_text_multiuser
+ )
+ )
+ } else if (uninstallFromAllUsers && !isSingleUser) {
+ messageBuilder.append(context.getString(R.string.uninstall_application_text_all_users))
+ } else if (uninstalledUser != myUserHandle) {
+ // Uninstalling user is issuing uninstall for another user
+ val customUserManager = context.createContextAsUser(uninstalledUser!!, 0)
+ .getSystemService(UserManager::class.java)
+ val userName = customUserManager!!.userName
+
+ val uninstalledUserType = getUninstalledUserType(myUserHandle, uninstalledUser!!)
+ val messageString: String
+ when (uninstalledUserType) {
+ UserManager.USER_TYPE_PROFILE_MANAGED -> {
+ messageString = context.getString(
+ R.string.uninstall_application_text_current_user_work_profile, userName
+ )
+ }
+
+ UserManager.USER_TYPE_PROFILE_CLONE -> {
+ isClonedApp = true
+ messageString = context.getString(
+ R.string.uninstall_application_text_current_user_clone_profile
+ )
+ }
+
+ else -> {
+ messageString = context.getString(
+ R.string.uninstall_application_text_user, userName
+ )
+ }
+
+ }
+ messageBuilder.append(messageString)
+ } else if (isCloneProfile(uninstalledUser!!)) {
+ isClonedApp = true
+ messageBuilder.append(context.getString(
+ R.string.uninstall_application_text_current_user_clone_profile
+ )
+ )
+ } else if (myUserHandle == UserHandle.SYSTEM
+ && hasClonedInstance(targetAppInfo!!.packageName)
+ ) {
+ messageBuilder.append(context.getString(
+ R.string.uninstall_application_text_with_clone_instance, targetAppLabel
+ )
+ )
+ } else {
+ messageBuilder.append(context.getString(R.string.uninstall_application_text))
+ }
+
+ val message = messageBuilder.toString()
+
+ val title = if (isClonedApp) {
+ context.getString(R.string.cloned_app_label, targetAppLabel)
+ } else {
+ targetAppLabel.toString()
+ }
+
+ var suggestToKeepAppData = false
+ try {
+ val pkgInfo = packageManager.getPackageInfo(targetPackageName!!, 0)
+ suggestToKeepAppData =
+ pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData()
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e)
+ }
+
+ var appDataSize: Long = 0
+ if (suggestToKeepAppData) {
+ appDataSize = getAppDataSize(
+ targetPackageName!!,
+ if (uninstallFromAllUsers) null else uninstalledUser
+ )
+ }
+
+ return UninstallUserActionRequired(title, message, appDataSize)
+ }
+
+ /**
+ * Returns whether there is only one "full" user on this device.
+ *
+ * **Note:** On devices that use [headless system user mode]
+ * [android.os.UserManager.isHeadlessSystemUserMode], the system user is not "full",
+ * so it's not be considered in the calculation.
+ */
+ private fun isSingleUser(): Boolean {
+ val userCount = userManager!!.userCount
+ return userCount == 1 || (UserManager.isHeadlessSystemUserMode() && userCount == 2)
+ }
+
+ /**
+ * Returns the type of the user from where an app is being uninstalled. We are concerned with
+ * only USER_TYPE_PROFILE_MANAGED and USER_TYPE_PROFILE_CLONE and whether the user and profile
+ * belong to the same profile group.
+ */
+ private fun getUninstalledUserType(
+ myUserHandle: UserHandle,
+ uninstalledUserHandle: UserHandle
+ ): String? {
+ if (!userManager!!.isSameProfileGroup(myUserHandle, uninstalledUserHandle)) {
+ return null
+ }
+ val customUserManager = context.createContextAsUser(uninstalledUserHandle, 0)
+ .getSystemService(UserManager::class.java)
+ val userTypes =
+ arrayOf(UserManager.USER_TYPE_PROFILE_MANAGED, UserManager.USER_TYPE_PROFILE_CLONE)
+
+ for (userType in userTypes) {
+ if (customUserManager!!.isUserOfType(userType)) {
+ return userType
+ }
+ }
+ return null
+ }
+
+ private fun hasClonedInstance(packageName: String): Boolean {
+ // Check if clone user is present on the device.
+ var cloneUser: UserHandle? = null
+ val profiles = userManager!!.userProfiles
+
+ for (userHandle in profiles) {
+ if (userHandle != UserHandle.SYSTEM && isCloneProfile(userHandle)) {
+ cloneUser = userHandle
+ break
+ }
+ }
+ // Check if another instance of given package exists in clone user profile.
+ return try {
+ cloneUser != null
+ && packageManager.getPackageUidAsUser(
+ packageName, PackageManager.PackageInfoFlags.of(0), cloneUser.identifier
+ ) > 0
+ } catch (e: PackageManager.NameNotFoundException) {
+ false
+ }
+ }
+
+ private fun isCloneProfile(userHandle: UserHandle): Boolean {
+ val customUserManager = context.createContextAsUser(userHandle, 0)
+ .getSystemService(UserManager::class.java)
+ return customUserManager!!.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)
+ }
+
+ /**
+ * Get number of bytes of the app data of the package.
+ *
+ * @param pkg The package that might have app data.
+ * @param user The user the package belongs to or `null` if files of all users should
+ * be counted.
+ * @return The number of bytes.
+ */
+ private fun getAppDataSize(pkg: String, user: UserHandle?): Long {
+ if (user != null) {
+ return getAppDataSizeForUser(pkg, user)
+ }
+ // We are uninstalling from all users. Get cumulative app data size for all users.
+ val userHandles = userManager!!.getUserHandles(true)
+ var totalAppDataSize: Long = 0
+ val numUsers = userHandles.size
+ for (i in 0 until numUsers) {
+ totalAppDataSize += getAppDataSizeForUser(pkg, userHandles[i])
+ }
+ return totalAppDataSize
+ }
+
+ /**
+ * Get number of bytes of the app data of the package.
+ *
+ * @param pkg The package that might have app data.
+ * @param user The user the package belongs to
+ * @return The number of bytes.
+ */
+ private fun getAppDataSizeForUser(pkg: String, user: UserHandle): Long {
+ val storageStatsManager = context.getSystemService(StorageStatsManager::class.java)
+ try {
+ val stats = storageStatsManager!!.queryStatsForPackage(
+ packageManager.getApplicationInfo(pkg, 0).storageUuid, pkg, user
+ )
+ return stats.getDataBytes()
+ } catch (e: Exception) {
+ Log.e(LOG_TAG, "Cannot determine amount of app data for $pkg", e)
+ }
+ return 0
+ }
+
+ fun initiateUninstall(keepData: Boolean) {
+ // Get an uninstallId to track results and show a notification on non-TV devices.
+ uninstallId = try {
+ UninstallEventReceiver.addObserver(
+ context, EventResultPersister.GENERATE_NEW_ID, this::handleUninstallResult
+ )
+ } catch (e: OutOfIdsException) {
+ Log.e(LOG_TAG, "Failed to start uninstall", e)
+ handleUninstallResult(
+ PackageInstaller.STATUS_FAILURE,
+ PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0
+ )
+ return
+ }
+
+ // TODO: Check with UX whether to show UninstallUninstalling dialog / notification?
+ uninstallResult.value = UninstallUninstalling(targetAppLabel, isClonedApp)
+
+ val uninstallData = Bundle()
+ uninstallData.putInt(EXTRA_UNINSTALL_ID, uninstallId)
+ uninstallData.putString(EXTRA_PACKAGE_NAME, targetPackageName)
+ uninstallData.putBoolean(Intent.EXTRA_UNINSTALL_ALL_USERS, uninstallFromAllUsers)
+ uninstallData.putCharSequence(EXTRA_APP_LABEL, targetAppLabel)
+ uninstallData.putBoolean(EXTRA_IS_CLONE_APP, isClonedApp)
+ Log.i(LOG_TAG, "Uninstalling extras = $uninstallData")
+
+ // Get a PendingIntent for result broadcast and issue an uninstall request
+ val broadcastIntent = Intent(BROADCAST_ACTION)
+ broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, uninstallId)
+ broadcastIntent.setPackage(context.packageName)
+ val pendingIntent = PendingIntent.getBroadcast(
+ context, uninstallId, broadcastIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ if (!startUninstall(
+ targetPackageName!!, uninstalledUser!!, pendingIntent, uninstallFromAllUsers,
+ keepData
+ )
+ ) {
+ handleUninstallResult(
+ PackageInstaller.STATUS_FAILURE,
+ PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0
+ )
+ }
+ }
+
+ private fun handleUninstallResult(
+ status: Int,
+ legacyStatus: Int,
+ message: String?,
+ serviceId: Int
+ ) {
+ if (callback != null) {
+ // The caller will be informed about the result via a callback
+ callback!!.onUninstallComplete(targetPackageName!!, legacyStatus, message)
+
+ // Since the caller already received the results, just finish the app at this point
+ uninstallResult.value = null
+ return
+ }
+ val returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
+ if (returnResult || callingActivity != null) {
+ val intent = Intent()
+ intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus)
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ uninstallResult.setValue(
+ UninstallSuccess(resultIntent = intent, activityResultCode = Activity.RESULT_OK)
+ )
+ } else {
+ uninstallResult.setValue(
+ UninstallFailed(
+ returnResult = true,
+ resultIntent = intent,
+ activityResultCode = Activity.RESULT_FIRST_USER
+ )
+ )
+ }
+ return
+ }
+
+ // Caller did not want the result back. So, we either show a Toast, or a Notification.
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ val statusMessage = if (isClonedApp) context.getString(
+ R.string.uninstall_done_clone_app, targetAppLabel
+ ) else context.getString(R.string.uninstall_done_app, targetAppLabel)
+ uninstallResult.setValue(
+ UninstallSuccess(activityResultCode = legacyStatus, message = statusMessage)
+ )
+ } else {
+ val uninstallFailureChannel = NotificationChannel(
+ UNINSTALL_FAILURE_CHANNEL,
+ context.getString(R.string.uninstall_failure_notification_channel),
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ notificationManager!!.createNotificationChannel(uninstallFailureChannel)
+
+ val uninstallFailedNotification: Notification.Builder =
+ Notification.Builder(context, UNINSTALL_FAILURE_CHANNEL)
+
+ val myUserHandle = Process.myUserHandle()
+ when (legacyStatus) {
+ PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER -> {
+ // Find out if the package is an active admin for some non-current user.
+ val otherBlockingUserHandle =
+ findUserOfDeviceAdmin(myUserHandle, targetPackageName!!)
+ if (otherBlockingUserHandle == null) {
+ Log.d(
+ LOG_TAG, "Uninstall failed because $targetPackageName"
+ + " is a device admin"
+ )
+ addDeviceManagerButton(context, uninstallFailedNotification)
+ setBigText(
+ uninstallFailedNotification, context.getString(
+ R.string.uninstall_failed_device_policy_manager
+ )
+ )
+ } else {
+ Log.d(
+ LOG_TAG, "Uninstall failed because $targetPackageName"
+ + " is a device admin of user $otherBlockingUserHandle"
+ )
+ val userName = context.createContextAsUser(otherBlockingUserHandle, 0)
+ .getSystemService(UserManager::class.java)!!.userName
+ setBigText(
+ uninstallFailedNotification, String.format(
+ context.getString(
+ R.string.uninstall_failed_device_policy_manager_of_user
+ ), userName
+ )
+ )
+ }
+ }
+
+ PackageManager.DELETE_FAILED_OWNER_BLOCKED -> {
+ val otherBlockingUserHandle = findBlockingUser(targetPackageName!!)
+ val isProfileOfOrSame = isProfileOfOrSame(
+ userManager!!, myUserHandle, otherBlockingUserHandle
+ )
+ if (isProfileOfOrSame) {
+ addDeviceManagerButton(context, uninstallFailedNotification)
+ } else {
+ addManageUsersButton(context, uninstallFailedNotification)
+ }
+ var bigText: String? = null
+ if (otherBlockingUserHandle == null) {
+ Log.d(
+ LOG_TAG, "Uninstall failed for $targetPackageName " +
+ "with code $status no blocking user"
+ )
+ } else if (otherBlockingUserHandle === UserHandle.SYSTEM) {
+ bigText = context.getString(R.string.uninstall_blocked_device_owner)
+ } else {
+ bigText = context.getString(
+ if (uninstallFromAllUsers) R.string.uninstall_all_blocked_profile_owner
+ else R.string.uninstall_blocked_profile_owner
+ )
+ }
+ bigText?.let { setBigText(uninstallFailedNotification, it) }
+ }
+
+ else -> {
+ Log.d(
+ LOG_TAG, "Uninstall blocked for $targetPackageName"
+ + " with legacy code $legacyStatus"
+ )
+ }
+ }
+ uninstallFailedNotification.setContentTitle(
+ context.getString(R.string.uninstall_failed_app, targetAppLabel)
+ )
+ uninstallFailedNotification.setOngoing(false)
+ uninstallFailedNotification.setSmallIcon(R.drawable.ic_error)
+
+ uninstallResult.setValue(
+ UninstallFailed(
+ returnResult = false,
+ uninstallNotificationId = uninstallId,
+ uninstallNotification = uninstallFailedNotification.build()
+ )
+ )
+ }
+ }
+
+ /**
+ * @param myUserHandle [UserHandle] of the current user.
+ * @param packageName Name of the package being uninstalled.
+ * @return the [UserHandle] of the user in which a package is a device admin.
+ */
+ private fun findUserOfDeviceAdmin(myUserHandle: UserHandle, packageName: String): UserHandle? {
+ for (otherUserHandle in userManager!!.getUserHandles(true)) {
+ // We only catch the case when the user in question is neither the
+ // current user nor its profile.
+ if (isProfileOfOrSame(userManager, myUserHandle, otherUserHandle)) {
+ continue
+ }
+ val dpm = context.createContextAsUser(otherUserHandle, 0)
+ .getSystemService(DevicePolicyManager::class.java)
+ if (dpm!!.packageHasActiveAdmins(packageName)) {
+ return otherUserHandle
+ }
+ }
+ return null
+ }
+
+ /**
+ *
+ * @param packageName Name of the package being uninstalled.
+ * @return [UserHandle] of the user in which a package is blocked from being uninstalled.
+ */
+ private fun findBlockingUser(packageName: String): UserHandle? {
+ for (otherUserHandle in userManager!!.getUserHandles(true)) {
+ // TODO (b/307399586): Add a negation when the logic of the method is fixed
+ if (packageManager.canUserUninstall(packageName, otherUserHandle)) {
+ return otherUserHandle
+ }
+ }
+ return null
+ }
+
+ /**
+ * Set big text for the notification.
+ *
+ * @param builder The builder of the notification
+ * @param text The text to set.
+ */
+ private fun setBigText(
+ builder: Notification.Builder,
+ text: CharSequence
+ ) {
+ builder.setStyle(Notification.BigTextStyle().bigText(text))
+ }
+
+ /**
+ * Add a button to the notification that links to the user management.
+ *
+ * @param context The context the notification is created in
+ * @param builder The builder of the notification
+ */
+ private fun addManageUsersButton(
+ context: Context,
+ builder: Notification.Builder
+ ) {
+ builder.addAction(
+ Notification.Action.Builder(
+ Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
+ context.getString(R.string.manage_users),
+ PendingIntent.getActivity(
+ context, 0, getUserSettingsIntent(),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ )
+ .build()
+ )
+ }
+
+ private fun getUserSettingsIntent(): Intent {
+ val intent = Intent(Settings.ACTION_USER_SETTINGS)
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_TASK)
+ return intent
+ }
+
+ /**
+ * Add a button to the notification that links to the device policy management.
+ *
+ * @param context The context the notification is created in
+ * @param builder The builder of the notification
+ */
+ private fun addDeviceManagerButton(
+ context: Context,
+ builder: Notification.Builder
+ ) {
+ builder.addAction(
+ Notification.Action.Builder(
+ Icon.createWithResource(context, R.drawable.ic_lock),
+ context.getString(R.string.manage_device_administrators),
+ PendingIntent.getActivity(
+ context, 0, getDeviceManagerIntent(),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ )
+ .build()
+ )
+ }
+
+ private fun getDeviceManagerIntent(): Intent {
+ val intent = Intent()
+ intent.setClassName(
+ "com.android.settings",
+ "com.android.settings.Settings\$DeviceAdminSettingsActivity"
+ )
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_TASK)
+ return intent
+ }
+
+ /**
+ * Starts an uninstall for the given package.
+ *
+ * @return `true` if there was no exception while uninstalling. This does not represent
+ * the result of the uninstall. Result will be made available in [handleUninstallResult]
+ */
+ private fun startUninstall(
+ packageName: String,
+ targetUser: UserHandle,
+ pendingIntent: PendingIntent,
+ uninstallFromAllUsers: Boolean,
+ keepData: Boolean
+ ): Boolean {
+ var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0
+ flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0
+
+ return try {
+ context.createContextAsUser(targetUser, 0)
+ .packageManager.packageInstaller.uninstall(
+ VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+ flags, pendingIntent.intentSender
+ )
+ true
+ } catch (e: IllegalArgumentException) {
+ Log.e(LOG_TAG, "Failed to uninstall", e)
+ false
+ }
+ }
+
+ fun cancelInstall() {
+ if (callback != null) {
+ callback!!.onUninstallComplete(
+ targetPackageName!!,
+ PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user"
+ )
+ }
+ }
+
+ companion object {
+ private val LOG_TAG = UninstallRepository::class.java.simpleName
+ private const val UNINSTALL_FAILURE_CHANNEL = "uninstall_failure"
+ private const val BROADCAST_ACTION = "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT"
+ private const val EXTRA_UNINSTALL_ID = "com.android.packageinstaller.extra.UNINSTALL_ID"
+ private const val EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL"
+ private const val EXTRA_IS_CLONE_APP = "com.android.packageinstaller.extra.IS_CLONE_APP"
+ private const val EXTRA_PACKAGE_NAME =
+ "com.android.packageinstaller.extra.EXTRA_PACKAGE_NAME"
+ }
+
+ class CallerInfo(val activityName: String?, val uid: Int)
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
new file mode 100644
index 0000000..f086209
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
@@ -0,0 +1,112 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.packageinstaller.v2.model
+
+import android.app.Activity
+import android.app.Notification
+import android.content.Intent
+import com.android.packageinstaller.R
+
+sealed class UninstallStage(val stageCode: Int) {
+
+ companion object {
+ const val STAGE_DEFAULT = -1
+ const val STAGE_ABORTED = 0
+ const val STAGE_READY = 1
+ const val STAGE_USER_ACTION_REQUIRED = 2
+ const val STAGE_UNINSTALLING = 3
+ const val STAGE_SUCCESS = 4
+ const val STAGE_FAILED = 5
+ }
+}
+
+class UninstallReady : UninstallStage(STAGE_READY)
+
+data class UninstallUserActionRequired(
+ val title: String? = null,
+ val message: String? = null,
+ val appDataSize: Long = 0
+) : UninstallStage(STAGE_USER_ACTION_REQUIRED)
+
+data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) :
+ UninstallStage(STAGE_UNINSTALLING)
+
+data class UninstallSuccess(
+ val resultIntent: Intent? = null,
+ val activityResultCode: Int = 0,
+ val message: String? = null,
+) : UninstallStage(STAGE_SUCCESS)
+
+data class UninstallFailed(
+ val returnResult: Boolean,
+ /**
+ * If the caller wants the result back, the intent will hold the uninstall failure status code
+ * and legacy code.
+ */
+ val resultIntent: Intent? = null,
+ val activityResultCode: Int = Activity.RESULT_CANCELED,
+ /**
+ * ID used to show [uninstallNotification]
+ */
+ val uninstallNotificationId: Int? = null,
+ /**
+ * When the user does not request a result back, this notification will be shown indicating the
+ * reason for uninstall failure.
+ */
+ val uninstallNotification: Notification? = null,
+) : UninstallStage(STAGE_FAILED) {
+
+ init {
+ if (uninstallNotification != null && uninstallNotificationId == null) {
+ throw IllegalArgumentException(
+ "uninstallNotification cannot be set without uninstallNotificationId"
+ )
+ }
+ }
+}
+
+data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED) {
+
+ var dialogTitleResource = 0
+ var dialogTextResource = 0
+ val activityResultCode = Activity.RESULT_FIRST_USER
+
+ init {
+ when (abortReason) {
+ ABORT_REASON_APP_UNAVAILABLE -> {
+ dialogTitleResource = R.string.app_not_found_dlg_title
+ dialogTextResource = R.string.app_not_found_dlg_text
+ }
+
+ ABORT_REASON_USER_NOT_ALLOWED -> {
+ dialogTitleResource = 0
+ dialogTextResource = R.string.user_is_not_allowed_dlg_text
+ }
+
+ else -> {
+ dialogTitleResource = 0
+ dialogTextResource = R.string.generic_error_dlg_text
+ }
+ }
+ }
+
+ companion object {
+ const val ABORT_REASON_GENERIC_ERROR = 0
+ const val ABORT_REASON_APP_UNAVAILABLE = 1
+ const val ABORT_REASON_USER_NOT_ALLOWED = 2
+ }
+}
+
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java
deleted file mode 100644
index 520b6c5..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallAborted.java
+++ /dev/null
@@ -1,127 +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.packageinstaller.v2.model.installstagedata;
-
-
-import android.app.Activity;
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class InstallAborted extends InstallStage {
-
- public static final int ABORT_REASON_INTERNAL_ERROR = 0;
- public static final int ABORT_REASON_POLICY = 1;
- public static final int ABORT_REASON_DONE = 2;
- public static final int DLG_PACKAGE_ERROR = 1;
- private final int mStage = InstallStage.STAGE_ABORTED;
- private final int mAbortReason;
-
- /**
- * It will hold the restriction name, when the restriction was enforced by the system, and not
- * a device admin.
- */
- @NonNull
- private final String mMessage;
- /**
- * <p>If abort reason is ABORT_REASON_POLICY, then this will hold the Intent
- * to display a support dialog when a feature was disabled by an admin. It will be
- * {@code null} if the feature is disabled by the system. In this case, the restriction name
- * will be set in {@link #mMessage} </p>
- *
- * <p>If the abort reason is ABORT_REASON_INTERNAL_ERROR, it <b>may</b> hold an
- * intent to be sent as a result to the calling activity.</p>
- */
- @Nullable
- private final Intent mIntent;
- private final int mErrorDialogType;
- private final int mActivityResultCode;
-
- private InstallAborted(int reason, @NonNull String message, @Nullable Intent intent,
- int activityResultCode, int errorDialogType) {
- mAbortReason = reason;
- mMessage = message;
- mIntent = intent;
- mErrorDialogType = errorDialogType;
- mActivityResultCode = activityResultCode;
- }
-
- public int getAbortReason() {
- return mAbortReason;
- }
-
- @NonNull
- public String getMessage() {
- return mMessage;
- }
-
- @Nullable
- public Intent getResultIntent() {
- return mIntent;
- }
-
- public int getErrorDialogType() {
- return mErrorDialogType;
- }
-
- public int getActivityResultCode() {
- return mActivityResultCode;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- public static class Builder {
-
- private final int mAbortReason;
- private String mMessage = "";
- private Intent mIntent = null;
- private int mActivityResultCode = Activity.RESULT_CANCELED;
- private int mErrorDialogType;
-
- public Builder(int reason) {
- mAbortReason = reason;
- }
-
- public Builder setMessage(@NonNull String message) {
- mMessage = message;
- return this;
- }
-
- public Builder setResultIntent(@NonNull Intent intent) {
- mIntent = intent;
- return this;
- }
-
- public Builder setErrorDialogType(int dialogType) {
- mErrorDialogType = dialogType;
- return this;
- }
-
- public Builder setActivityResultCode(int resultCode) {
- mActivityResultCode = resultCode;
- return this;
- }
-
- public InstallAborted build() {
- return new InstallAborted(mAbortReason, mMessage, mIntent, mActivityResultCode,
- mErrorDialogType);
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java
deleted file mode 100644
index 67e1690..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallFailed.java
+++ /dev/null
@@ -1,69 +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.packageinstaller.v2.model.installstagedata;
-
-import android.graphics.drawable.Drawable;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallFailed extends InstallStage {
-
- private final int mStage = InstallStage.STAGE_FAILED;
- @NonNull
- private final AppSnippet mAppSnippet;
- private final int mStatusCode;
- private final int mLegacyCode;
- @Nullable
- private final String mMessage;
-
- public InstallFailed(@NonNull AppSnippet appSnippet, int statusCode, int legacyCode,
- @Nullable String message) {
- mAppSnippet = appSnippet;
- mLegacyCode = statusCode;
- mStatusCode = legacyCode;
- mMessage = message;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- @NonNull
- public Drawable getAppIcon() {
- return mAppSnippet.getIcon();
- }
-
- @NonNull
- public String getAppLabel() {
- return (String) mAppSnippet.getLabel();
- }
-
- public int getStatusCode() {
- return mStatusCode;
- }
-
- public int getLegacyCode() {
- return mLegacyCode;
- }
-
- @Nullable
- public String getMessage() {
- return mMessage;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java
deleted file mode 100644
index efd4947..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallInstalling.java
+++ /dev/null
@@ -1,47 +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.packageinstaller.v2.model.installstagedata;
-
-import android.graphics.drawable.Drawable;
-import androidx.annotation.NonNull;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallInstalling extends InstallStage {
-
- private final int mStage = InstallStage.STAGE_INSTALLING;
- @NonNull
- private final AppSnippet mAppSnippet;
-
- public InstallInstalling(@NonNull AppSnippet appSnippet) {
- mAppSnippet = appSnippet;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- @NonNull
- public Drawable getAppIcon() {
- return mAppSnippet.getIcon();
- }
-
- @NonNull
- public String getAppLabel() {
- return (String) mAppSnippet.getLabel();
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java
deleted file mode 100644
index 548f2c5..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallReady.java
+++ /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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model.installstagedata;
-
-public class InstallReady extends InstallStage{
-
- private final int mStage = InstallStage.STAGE_READY;
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java
deleted file mode 100644
index f91e64b..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStage.java
+++ /dev/null
@@ -1,34 +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.packageinstaller.v2.model.installstagedata;
-
-public abstract class InstallStage {
-
- public static final int STAGE_DEFAULT = -1;
- public static final int STAGE_ABORTED = 0;
- public static final int STAGE_STAGING = 1;
- public static final int STAGE_READY = 2;
- public static final int STAGE_USER_ACTION_REQUIRED = 3;
- public static final int STAGE_INSTALLING = 4;
- public static final int STAGE_SUCCESS = 5;
- public static final int STAGE_FAILED = 6;
-
- /**
- * @return the integer value representing current install stage.
- */
- public abstract int getStageCode();
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java
deleted file mode 100644
index a979cf8..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallStaging.java
+++ /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.packageinstaller.v2.model.installstagedata;
-
-public class InstallStaging extends InstallStage {
-
- private final int mStage = InstallStage.STAGE_STAGING;
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java
deleted file mode 100644
index da48256..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallSuccess.java
+++ /dev/null
@@ -1,95 +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.packageinstaller.v2.model.installstagedata;
-
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import androidx.annotation.NonNull;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallSuccess extends InstallStage {
-
- private final int mStage = InstallStage.STAGE_SUCCESS;
-
- @NonNull
- private final AppSnippet mAppSnippet;
- private final boolean mShouldReturnResult;
- /**
- * <p>If the caller is requesting a result back, this will hold the Intent with
- * EXTRA_INSTALL_RESULT set to INSTALL_SUCCEEDED which is sent back to the caller.</p>
- * <p>If the caller doesn't want the result back, this will hold the Intent that launches
- * the newly installed / updated app.</p>
- */
- @NonNull
- private final Intent mResultIntent;
-
- public InstallSuccess(@NonNull AppSnippet appSnippet, boolean shouldReturnResult,
- @NonNull Intent launcherIntent) {
- mAppSnippet = appSnippet;
- mShouldReturnResult = shouldReturnResult;
- mResultIntent = launcherIntent;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- @NonNull
- public Drawable getAppIcon() {
- return mAppSnippet.getIcon();
- }
-
- @NonNull
- public String getAppLabel() {
- return (String) mAppSnippet.getLabel();
- }
-
- public boolean shouldReturnResult() {
- return mShouldReturnResult;
- }
-
- @NonNull
- public Intent getResultIntent() {
- return mResultIntent;
- }
-
- public static class Builder {
-
- private final AppSnippet mAppSnippet;
- private boolean mShouldReturnResult;
- private Intent mLauncherIntent;
-
- public Builder(@NonNull AppSnippet appSnippet) {
- mAppSnippet = appSnippet;
- }
-
- public Builder setShouldReturnResult(boolean returnResult) {
- mShouldReturnResult = returnResult;
- return this;
- }
-
- public Builder setResultIntent(@NonNull Intent intent) {
- mLauncherIntent = intent;
- return this;
- }
-
- public InstallSuccess build() {
- return new InstallSuccess(mAppSnippet, mShouldReturnResult, mLauncherIntent);
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java
deleted file mode 100644
index 08a7487..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/installstagedata/InstallUserActionRequired.java
+++ /dev/null
@@ -1,99 +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.packageinstaller.v2.model.installstagedata;
-
-import android.graphics.drawable.Drawable;
-import androidx.annotation.Nullable;
-import com.android.packageinstaller.v2.model.PackageUtil.AppSnippet;
-
-public class InstallUserActionRequired extends InstallStage {
-
- public static final int USER_ACTION_REASON_UNKNOWN_SOURCE = 0;
- public static final int USER_ACTION_REASON_ANONYMOUS_SOURCE = 1;
- public static final int USER_ACTION_REASON_INSTALL_CONFIRMATION = 2;
- private final int mStage = InstallStage.STAGE_USER_ACTION_REQUIRED;
- private final int mActionReason;
- @Nullable
- private final AppSnippet mAppSnippet;
- private final boolean mIsAppUpdating;
- @Nullable
- private final String mDialogMessage;
-
- public InstallUserActionRequired(int actionReason, @Nullable AppSnippet appSnippet,
- boolean isUpdating, @Nullable String dialogMessage) {
- mActionReason = actionReason;
- mAppSnippet = appSnippet;
- mIsAppUpdating = isUpdating;
- mDialogMessage = dialogMessage;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- @Nullable
- public Drawable getAppIcon() {
- return mAppSnippet != null ? mAppSnippet.getIcon() : null;
- }
-
- @Nullable
- public String getAppLabel() {
- return mAppSnippet != null ? (String) mAppSnippet.getLabel() : null;
- }
-
- public boolean isAppUpdating() {
- return mIsAppUpdating;
- }
-
- @Nullable
- public String getDialogMessage() {
- return mDialogMessage;
- }
-
- public int getActionReason() {
- return mActionReason;
- }
-
- public static class Builder {
-
- private final int mActionReason;
- private final AppSnippet mAppSnippet;
- private boolean mIsAppUpdating;
- private String mDialogMessage;
-
- public Builder(int actionReason, @Nullable AppSnippet appSnippet) {
- mActionReason = actionReason;
- mAppSnippet = appSnippet;
- }
-
- public Builder setAppUpdating(boolean isUpdating) {
- mIsAppUpdating = isUpdating;
- return this;
- }
-
- public Builder setDialogMessage(@Nullable String message) {
- mDialogMessage = message;
- return this;
- }
-
- public InstallUserActionRequired build() {
- return new InstallUserActionRequired(mActionReason, mAppSnippet, mIsAppUpdating,
- mDialogMessage);
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallAborted.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallAborted.java
deleted file mode 100644
index 9aea6b1..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallAborted.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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model.uninstallstagedata;
-
-import android.app.Activity;
-import com.android.packageinstaller.R;
-
-public class UninstallAborted extends UninstallStage {
-
- public static final int ABORT_REASON_GENERIC_ERROR = 0;
- public static final int ABORT_REASON_APP_UNAVAILABLE = 1;
- public static final int ABORT_REASON_USER_NOT_ALLOWED = 2;
- private final int mStage = UninstallStage.STAGE_ABORTED;
- private final int mAbortReason;
- private final int mDialogTitleResource;
- private final int mDialogTextResource;
- private final int mActivityResultCode = Activity.RESULT_FIRST_USER;
-
- public UninstallAborted(int abortReason) {
- mAbortReason = abortReason;
- switch (abortReason) {
- case ABORT_REASON_APP_UNAVAILABLE -> {
- mDialogTitleResource = R.string.app_not_found_dlg_title;
- mDialogTextResource = R.string.app_not_found_dlg_text;
- }
- case ABORT_REASON_USER_NOT_ALLOWED -> {
- mDialogTitleResource = 0;
- mDialogTextResource = R.string.user_is_not_allowed_dlg_text;
- }
- default -> {
- mDialogTitleResource = 0;
- mDialogTextResource = R.string.generic_error_dlg_text;
- }
- }
- }
-
- public int getAbortReason() {
- return mAbortReason;
- }
-
- public int getActivityResultCode() {
- return mActivityResultCode;
- }
-
- public int getDialogTitleResource() {
- return mDialogTitleResource;
- }
-
- public int getDialogTextResource() {
- return mDialogTextResource;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java
deleted file mode 100644
index 6ed8883..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java
+++ /dev/null
@@ -1,119 +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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model.uninstallstagedata;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.content.Intent;
-
-public class UninstallFailed extends UninstallStage {
-
- private final int mStage = UninstallStage.STAGE_FAILED;
- private final boolean mReturnResult;
- /**
- * If the caller wants the result back, the intent will hold the uninstall failure status code
- * and legacy code.
- */
- private final Intent mResultIntent;
- /**
- * When the user does not request a result back, this notification will be shown indicating the
- * reason for uninstall failure.
- */
- private final Notification mUninstallNotification;
- /**
- * ID used to show {@link #mUninstallNotification}
- */
- private final int mUninstallId;
- private final int mActivityResultCode;
-
- public UninstallFailed(boolean returnResult, Intent resultIntent, int activityResultCode,
- int uninstallId, Notification uninstallNotification) {
- mReturnResult = returnResult;
- mResultIntent = resultIntent;
- mActivityResultCode = activityResultCode;
- mUninstallId = uninstallId;
- mUninstallNotification = uninstallNotification;
- }
-
- public boolean returnResult() {
- return mReturnResult;
- }
-
- public Intent getResultIntent() {
- return mResultIntent;
- }
-
- public int getActivityResultCode() {
- return mActivityResultCode;
- }
-
- public Notification getUninstallNotification() {
- return mUninstallNotification;
- }
-
- public int getUninstallId() {
- return mUninstallId;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- public static class Builder {
-
- private final boolean mReturnResult;
- private int mActivityResultCode = Activity.RESULT_CANCELED;
- /**
- * See {@link UninstallFailed#mResultIntent}
- */
- private Intent mResultIntent = null;
- /**
- * See {@link UninstallFailed#mUninstallNotification}
- */
- private Notification mUninstallNotification;
- /**
- * See {@link UninstallFailed#mUninstallId}
- */
- private int mUninstallId;
-
- public Builder(boolean returnResult) {
- mReturnResult = returnResult;
- }
-
- public Builder setUninstallNotification(int uninstallId, Notification notification) {
- mUninstallId = uninstallId;
- mUninstallNotification = notification;
- return this;
- }
-
- public Builder setResultIntent(Intent intent) {
- mResultIntent = intent;
- return this;
- }
-
- public Builder setActivityResultCode(int resultCode) {
- mActivityResultCode = resultCode;
- return this;
- }
-
- public UninstallFailed build() {
- return new UninstallFailed(mReturnResult, mResultIntent, mActivityResultCode,
- mUninstallId, mUninstallNotification);
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallReady.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallReady.java
deleted file mode 100644
index 0108cb4..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallReady.java
+++ /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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model.uninstallstagedata;
-
-public class UninstallReady extends UninstallStage {
-
- private final int mStage = UninstallStage.STAGE_READY;
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallStage.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallStage.java
deleted file mode 100644
index 87ca4ec..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallStage.java
+++ /dev/null
@@ -1,30 +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.packageinstaller.v2.model.uninstallstagedata;
-
-public abstract class UninstallStage {
-
- public static final int STAGE_DEFAULT = -1;
- public static final int STAGE_ABORTED = 0;
- public static final int STAGE_READY = 1;
- public static final int STAGE_USER_ACTION_REQUIRED = 2;
- public static final int STAGE_UNINSTALLING = 3;
- public static final int STAGE_SUCCESS = 4;
- public static final int STAGE_FAILED = 5;
-
- public abstract int getStageCode();
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java
deleted file mode 100644
index 5df6b02..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java
+++ /dev/null
@@ -1,79 +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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model.uninstallstagedata;
-
-import android.content.Intent;
-
-public class UninstallSuccess extends UninstallStage {
-
- private final int mStage = UninstallStage.STAGE_SUCCESS;
- private final String mMessage;
- private final Intent mResultIntent;
- private final int mActivityResultCode;
-
- public UninstallSuccess(Intent resultIntent, int activityResultCode, String message) {
- mResultIntent = resultIntent;
- mActivityResultCode = activityResultCode;
- mMessage = message;
- }
-
- public String getMessage() {
- return mMessage;
- }
-
- public Intent getResultIntent() {
- return mResultIntent;
- }
-
- public int getActivityResultCode() {
- return mActivityResultCode;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- public static class Builder {
-
- private Intent mResultIntent;
- private int mActivityResultCode;
- private String mMessage;
-
- public Builder() {
- }
-
- public Builder setResultIntent(Intent intent) {
- mResultIntent = intent;
- return this;
- }
-
- public Builder setActivityResultCode(int resultCode) {
- mActivityResultCode = resultCode;
- return this;
- }
-
- public Builder setMessage(String message) {
- mMessage = message;
- return this;
- }
-
- public UninstallSuccess build() {
- return new UninstallSuccess(mResultIntent, mActivityResultCode, mMessage);
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUninstalling.java
deleted file mode 100644
index f5156cb..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUninstalling.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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model.uninstallstagedata;
-
-public class UninstallUninstalling extends UninstallStage {
-
- private final int mStage = UninstallStage.STAGE_UNINSTALLING;
-
- private final CharSequence mAppLabel;
- private final boolean mIsCloneUser;
-
- public UninstallUninstalling(CharSequence appLabel, boolean isCloneUser) {
- mAppLabel = appLabel;
- mIsCloneUser = isCloneUser;
- }
-
- public CharSequence getAppLabel() {
- return mAppLabel;
- }
-
- public boolean isCloneUser() {
- return mIsCloneUser;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java
deleted file mode 100644
index b600149..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.model.uninstallstagedata;
-
-public class UninstallUserActionRequired extends UninstallStage {
-
- private final int mStage = UninstallStage.STAGE_USER_ACTION_REQUIRED;
- private final String mTitle;
- private final String mMessage;
- private final long mAppDataSize;
-
- public UninstallUserActionRequired(String title, String message, long appDataSize) {
- mTitle = title;
- mMessage = message;
- mAppDataSize = appDataSize;
- }
-
- public String getTitle() {
- return mTitle;
- }
-
- public String getMessage() {
- return mMessage;
- }
-
- public long getAppDataSize() {
- return mAppDataSize;
- }
-
- @Override
- public int getStageCode() {
- return mStage;
- }
-
- public static class Builder {
-
- private String mTitle;
- private String mMessage;
- private long mAppDataSize = 0;
-
- public Builder setTitle(String title) {
- mTitle = title;
- return this;
- }
-
- public Builder setMessage(String message) {
- mMessage = message;
- return this;
- }
-
- public Builder setAppDataSize(long appDataSize) {
- mAppDataSize = appDataSize;
- return this;
- }
-
- public UninstallUserActionRequired build() {
- return new UninstallUserActionRequired(mTitle, mMessage, mAppDataSize);
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.java
deleted file mode 100644
index fdb024f..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.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.packageinstaller.v2.ui;
-
-import android.content.Intent;
-
-public interface InstallActionListener {
-
- /**
- * Method to handle a positive response from the user
- */
- void onPositiveResponse(int stageCode);
-
- /**
- * Method to dispatch intent for toggling "install from unknown sources" setting for a package
- */
- void sendUnknownAppsIntent(String packageName);
-
- /**
- * Method to handle a negative response from the user
- */
- void onNegativeResponse(int stageCode);
- void openInstalledApp(Intent intent);
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt
new file mode 100644
index 0000000..c109fc6
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallActionListener.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.packageinstaller.v2.ui
+
+import android.content.Intent
+
+interface InstallActionListener {
+ /**
+ * Method to handle a positive response from the user.
+ */
+ fun onPositiveResponse(reasonCode: Int)
+
+ /**
+ * Method to dispatch intent for toggling "install from unknown sources" setting for a package.
+ */
+ fun sendUnknownAppsIntent(sourcePackageName: String)
+
+ /**
+ * Method to handle a negative response from the user.
+ */
+ fun onNegativeResponse(stageCode: Int)
+
+ /**
+ * Launch the intent to open the newly installed / updated app.
+ */
+ fun openInstalledApp(intent: Intent?)
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java
deleted file mode 100644
index d06b4b3..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.java
+++ /dev/null
@@ -1,354 +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.packageinstaller.v2.ui;
-
-import static android.content.Intent.CATEGORY_LAUNCHER;
-import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
-import static android.os.Process.INVALID_UID;
-import static com.android.packageinstaller.v2.model.InstallRepository.EXTRA_STAGED_SESSION_ID;
-
-import android.app.Activity;
-import android.app.AppOpsManager;
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.Window;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.InstallRepository;
-import com.android.packageinstaller.v2.model.InstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.installstagedata.InstallAborted;
-import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
-import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
-import com.android.packageinstaller.v2.ui.fragments.AnonymousSourceFragment;
-import com.android.packageinstaller.v2.ui.fragments.ExternalSourcesBlockedFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallConfirmationFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallFailedFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallInstallingFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallStagingFragment;
-import com.android.packageinstaller.v2.ui.fragments.InstallSuccessFragment;
-import com.android.packageinstaller.v2.ui.fragments.SimpleErrorFragment;
-import com.android.packageinstaller.v2.viewmodel.InstallViewModel;
-import com.android.packageinstaller.v2.viewmodel.InstallViewModelFactory;
-import java.util.ArrayList;
-import java.util.List;
-
-public class InstallLaunch extends FragmentActivity implements InstallActionListener {
-
- public static final String EXTRA_CALLING_PKG_UID =
- InstallLaunch.class.getPackageName() + ".callingPkgUid";
- public static final String EXTRA_CALLING_PKG_NAME =
- InstallLaunch.class.getPackageName() + ".callingPkgName";
- private static final String TAG = InstallLaunch.class.getSimpleName();
- private static final String TAG_DIALOG = "dialog";
- private final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
- private final boolean mLocalLOGV = false;
- /**
- * A collection of unknown sources listeners that are actively listening for app ops mode
- * changes
- */
- private final List<UnknownSourcesListener> mActiveUnknownSourcesListeners = new ArrayList<>(1);
- private InstallViewModel mInstallViewModel;
- private InstallRepository mInstallRepository;
- private FragmentManager mFragmentManager;
- private AppOpsManager mAppOpsManager;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- this.requestWindowFeature(Window.FEATURE_NO_TITLE);
-
- mFragmentManager = getSupportFragmentManager();
- mAppOpsManager = getSystemService(AppOpsManager.class);
-
- mInstallRepository = new InstallRepository(getApplicationContext());
- mInstallViewModel = new ViewModelProvider(this,
- new InstallViewModelFactory(this.getApplication(), mInstallRepository)).get(
- InstallViewModel.class);
-
- Intent intent = getIntent();
- CallerInfo info = new CallerInfo(
- intent.getStringExtra(EXTRA_CALLING_PKG_NAME),
- intent.getIntExtra(EXTRA_CALLING_PKG_UID, INVALID_UID));
- mInstallViewModel.preprocessIntent(intent, info);
-
- mInstallViewModel.getCurrentInstallStage().observe(this, this::onInstallStageChange);
- }
-
- /**
- * Main controller of the UI. This method shows relevant dialogs based on the install stage
- */
- private void onInstallStageChange(InstallStage installStage) {
- switch (installStage.getStageCode()) {
- case InstallStage.STAGE_STAGING -> {
- InstallStagingFragment stagingDialog = new InstallStagingFragment();
- showDialogInner(stagingDialog);
- mInstallViewModel.getStagingProgress().observe(this, stagingDialog::setProgress);
- }
- case InstallStage.STAGE_ABORTED -> {
- InstallAborted aborted = (InstallAborted) installStage;
- switch (aborted.getAbortReason()) {
- // TODO: check if any dialog is to be shown for ABORT_REASON_INTERNAL_ERROR
- case InstallAborted.ABORT_REASON_DONE,
- InstallAborted.ABORT_REASON_INTERNAL_ERROR ->
- setResult(aborted.getActivityResultCode(), aborted.getResultIntent(), true);
- case InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted);
- default -> setResult(RESULT_CANCELED, null, true);
- }
- }
- case InstallStage.STAGE_USER_ACTION_REQUIRED -> {
- InstallUserActionRequired uar = (InstallUserActionRequired) installStage;
- switch (uar.getActionReason()) {
- case InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION -> {
- InstallConfirmationFragment actionDialog =
- new InstallConfirmationFragment(uar);
- showDialogInner(actionDialog);
- }
- case InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE -> {
- ExternalSourcesBlockedFragment externalSourceDialog =
- new ExternalSourcesBlockedFragment(uar);
- showDialogInner(externalSourceDialog);
- }
- case InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE -> {
- AnonymousSourceFragment anonymousSourceDialog =
- new AnonymousSourceFragment();
- showDialogInner(anonymousSourceDialog);
- }
- }
- }
- case InstallStage.STAGE_INSTALLING -> {
- InstallInstalling installing = (InstallInstalling) installStage;
- InstallInstallingFragment installingDialog =
- new InstallInstallingFragment(installing);
- showDialogInner(installingDialog);
- }
- case InstallStage.STAGE_SUCCESS -> {
- InstallSuccess success = (InstallSuccess) installStage;
- if (success.shouldReturnResult()) {
- Intent successIntent = success.getResultIntent();
- setResult(Activity.RESULT_OK, successIntent, true);
- } else {
- InstallSuccessFragment successFragment = new InstallSuccessFragment(success);
- showDialogInner(successFragment);
- }
- }
- case InstallStage.STAGE_FAILED -> {
- InstallFailed failed = (InstallFailed) installStage;
- InstallFailedFragment failedDialog = new InstallFailedFragment(failed);
- showDialogInner(failedDialog);
- }
- default -> {
- Log.d(TAG, "Unimplemented stage: " + installStage.getStageCode());
- showDialogInner(null);
- }
- }
- }
-
- private void showPolicyRestrictionDialog(InstallAborted aborted) {
- String restriction = aborted.getMessage();
- Intent adminSupportIntent = aborted.getResultIntent();
- boolean shouldFinish;
-
- // If the given restriction is set by an admin, display information about the
- // admin enforcing the restriction for the affected user. If not enforced by the admin,
- // show the system dialog.
- if (adminSupportIntent != null) {
- if (mLocalLOGV) {
- Log.i(TAG, "Restriction set by admin, starting " + adminSupportIntent);
- }
- startActivity(adminSupportIntent);
- // Finish the package installer app since the next dialog will not be shown by this app
- shouldFinish = true;
- } else {
- if (mLocalLOGV) {
- Log.i(TAG, "Restriction set by system: " + restriction);
- }
- DialogFragment blockedByPolicyDialog = createDevicePolicyRestrictionDialog(restriction);
- // Don't finish the package installer app since the next dialog
- // will be shown by this app
- shouldFinish = false;
- showDialogInner(blockedByPolicyDialog);
- }
- setResult(RESULT_CANCELED, null, shouldFinish);
- }
-
- /**
- * Create a new dialog based on the install restriction enforced.
- *
- * @param restriction The restriction to create the dialog for
- * @return The dialog
- */
- private DialogFragment createDevicePolicyRestrictionDialog(String restriction) {
- if (mLocalLOGV) {
- Log.i(TAG, "createDialog(" + restriction + ")");
- }
- return switch (restriction) {
- case UserManager.DISALLOW_INSTALL_APPS ->
- new SimpleErrorFragment(R.string.install_apps_user_restriction_dlg_text);
- case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY ->
- new SimpleErrorFragment(R.string.unknown_apps_user_restriction_dlg_text);
- default -> null;
- };
- }
-
- /**
- * Replace any visible dialog by the dialog returned by InstallRepository
- *
- * @param newDialog The new dialog to display
- */
- private void showDialogInner(@Nullable DialogFragment newDialog) {
- DialogFragment currentDialog = (DialogFragment) mFragmentManager.findFragmentByTag(
- TAG_DIALOG);
- if (currentDialog != null) {
- currentDialog.dismissAllowingStateLoss();
- }
- if (newDialog != null) {
- newDialog.show(mFragmentManager, TAG_DIALOG);
- }
- }
-
- public void setResult(int resultCode, Intent data, boolean shouldFinish) {
- super.setResult(resultCode, data);
- if (shouldFinish) {
- finish();
- }
- }
-
- @Override
- public void onPositiveResponse(int reasonCode) {
- switch (reasonCode) {
- case InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE ->
- mInstallViewModel.forcedSkipSourceCheck();
- case InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION ->
- mInstallViewModel.initiateInstall();
- }
- }
-
- @Override
- public void onNegativeResponse(int stageCode) {
- if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
- mInstallViewModel.cleanupInstall();
- }
- setResult(Activity.RESULT_CANCELED, null, true);
- }
-
- @Override
- public void sendUnknownAppsIntent(String sourcePackageName) {
- Intent settingsIntent = new Intent();
- settingsIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
- final Uri packageUri = Uri.parse("package:" + sourcePackageName);
- settingsIntent.setData(packageUri);
- settingsIntent.setFlags(FLAG_ACTIVITY_NO_HISTORY);
-
- try {
- registerAppOpChangeListener(new UnknownSourcesListener(sourcePackageName),
- sourcePackageName);
- startActivityForResult(settingsIntent, REQUEST_TRUST_EXTERNAL_SOURCE);
- } catch (ActivityNotFoundException exc) {
- Log.e(TAG, "Settings activity not found for action: "
- + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
- }
- }
-
- @Override
- public void openInstalledApp(Intent intent) {
- setResult(RESULT_OK, intent, true);
- if (intent != null && intent.hasCategory(CATEGORY_LAUNCHER)) {
- startActivity(intent);
- }
- }
-
- private void registerAppOpChangeListener(UnknownSourcesListener listener, String packageName) {
- mAppOpsManager.startWatchingMode(
- AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, packageName,
- listener);
- mActiveUnknownSourcesListeners.add(listener);
- }
-
- private void unregisterAppOpChangeListener(UnknownSourcesListener listener) {
- mActiveUnknownSourcesListeners.remove(listener);
- mAppOpsManager.stopWatchingMode(listener);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_TRUST_EXTERNAL_SOURCE) {
- mInstallViewModel.reattemptInstall();
- } else {
- setResult(Activity.RESULT_CANCELED, null, true);
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- while (!mActiveUnknownSourcesListeners.isEmpty()) {
- unregisterAppOpChangeListener(mActiveUnknownSourcesListeners.get(0));
- }
- }
-
- private class UnknownSourcesListener implements AppOpsManager.OnOpChangedListener {
-
- private final String mOriginatingPackage;
-
- public UnknownSourcesListener(String originatingPackage) {
- mOriginatingPackage = originatingPackage;
- }
-
- @Override
- public void onOpChanged(String op, String packageName) {
- if (!mOriginatingPackage.equals(packageName)) {
- return;
- }
- unregisterAppOpChangeListener(this);
- mActiveUnknownSourcesListeners.remove(this);
- if (isDestroyed()) {
- return;
- }
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- if (!isDestroyed()) {
- // Relaunch Pia to continue installation.
- startActivity(getIntent()
- .putExtra(EXTRA_STAGED_SESSION_ID, mInstallViewModel.getStagedSessionId()));
-
- // If the userId of the root of activity stack is different from current userId,
- // starting Pia again lead to duplicate instances of the app in the stack.
- // As such, finish the old instance. Old Pia is finished even if the userId of
- // the root is the same, since there is no way to determine the difference in
- // userIds.
- finish();
- }
- }, 500);
- }
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
new file mode 100644
index 0000000..2b610d7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
@@ -0,0 +1,348 @@
+/*
+ * 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.packageinstaller.v2.ui
+
+import android.app.Activity
+import android.app.AppOpsManager
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Process
+import android.os.UserManager
+import android.provider.Settings
+import android.util.Log
+import android.view.Window
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.R
+import com.android.packageinstaller.v2.model.InstallRepository
+import com.android.packageinstaller.v2.model.InstallAborted
+import com.android.packageinstaller.v2.model.InstallFailed
+import com.android.packageinstaller.v2.model.InstallInstalling
+import com.android.packageinstaller.v2.model.InstallStage
+import com.android.packageinstaller.v2.model.InstallSuccess
+import com.android.packageinstaller.v2.model.InstallUserActionRequired
+import com.android.packageinstaller.v2.ui.fragments.AnonymousSourceFragment
+import com.android.packageinstaller.v2.ui.fragments.ExternalSourcesBlockedFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallConfirmationFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallFailedFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallInstallingFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallStagingFragment
+import com.android.packageinstaller.v2.ui.fragments.InstallSuccessFragment
+import com.android.packageinstaller.v2.ui.fragments.SimpleErrorFragment
+import com.android.packageinstaller.v2.viewmodel.InstallViewModel
+import com.android.packageinstaller.v2.viewmodel.InstallViewModelFactory
+
+class InstallLaunch : FragmentActivity(), InstallActionListener {
+
+ companion object {
+ @JvmField val EXTRA_CALLING_PKG_UID =
+ InstallLaunch::class.java.packageName + ".callingPkgUid"
+ @JvmField val EXTRA_CALLING_PKG_NAME =
+ InstallLaunch::class.java.packageName + ".callingPkgName"
+ private val LOG_TAG = InstallLaunch::class.java.simpleName
+ private const val TAG_DIALOG = "dialog"
+ }
+
+ private val localLOGV = false
+
+ /**
+ * A collection of unknown sources listeners that are actively listening for app ops mode
+ * changes
+ */
+ private val activeUnknownSourcesListeners: MutableList<UnknownSourcesListener> = ArrayList(1)
+ private var installViewModel: InstallViewModel? = null
+ private var installRepository: InstallRepository? = null
+ private var fragmentManager: FragmentManager? = null
+ private var appOpsManager: AppOpsManager? = null
+ private lateinit var unknownAppsIntentLauncher: ActivityResultLauncher<Intent>
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ requestWindowFeature(Window.FEATURE_NO_TITLE)
+ fragmentManager = supportFragmentManager
+ appOpsManager = getSystemService(AppOpsManager::class.java)
+ installRepository = InstallRepository(applicationContext)
+ installViewModel = ViewModelProvider(
+ this, InstallViewModelFactory(this.application, installRepository!!)
+ )[InstallViewModel::class.java]
+
+ val intent = intent
+ val info = InstallRepository.CallerInfo(
+ intent.getStringExtra(EXTRA_CALLING_PKG_NAME),
+ intent.getIntExtra(EXTRA_CALLING_PKG_UID, Process.INVALID_UID)
+ )
+ installViewModel!!.preprocessIntent(intent, info)
+ installViewModel!!.currentInstallStage.observe(this) { installStage: InstallStage ->
+ onInstallStageChange(installStage)
+ }
+
+ // Used to launch intent for Settings, to manage "install unknown apps" permission
+ unknownAppsIntentLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ // Reattempt installation on coming back from Settings, after toggling
+ // "install unknown apps" permission
+ installViewModel!!.reattemptInstall()
+ }
+ }
+
+ /**
+ * Main controller of the UI. This method shows relevant dialogs based on the install stage
+ */
+ private fun onInstallStageChange(installStage: InstallStage) {
+ when (installStage.stageCode) {
+ InstallStage.STAGE_STAGING -> {
+ val stagingDialog = InstallStagingFragment()
+ showDialogInner(stagingDialog)
+ installViewModel!!.stagingProgress.observe(this) { progress: Int ->
+ stagingDialog.setProgress(progress)
+ }
+ }
+
+ InstallStage.STAGE_ABORTED -> {
+ val aborted = installStage as InstallAborted
+ when (aborted.abortReason) {
+ InstallAborted.ABORT_REASON_DONE, InstallAborted.ABORT_REASON_INTERNAL_ERROR ->
+ setResult(aborted.activityResultCode, aborted.resultIntent, true)
+
+ InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted)
+ else -> setResult(Activity.RESULT_CANCELED, null, true)
+ }
+ }
+
+ InstallStage.STAGE_USER_ACTION_REQUIRED -> {
+ val uar = installStage as InstallUserActionRequired
+ when (uar.actionReason) {
+ InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION -> {
+ val actionDialog = InstallConfirmationFragment(uar)
+ showDialogInner(actionDialog)
+ }
+
+ InstallUserActionRequired.USER_ACTION_REASON_UNKNOWN_SOURCE -> {
+ val externalSourceDialog = ExternalSourcesBlockedFragment(uar)
+ showDialogInner(externalSourceDialog)
+ }
+
+ InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE -> {
+ val anonymousSourceDialog = AnonymousSourceFragment()
+ showDialogInner(anonymousSourceDialog)
+ }
+ }
+ }
+
+ InstallStage.STAGE_INSTALLING -> {
+ val installing = installStage as InstallInstalling
+ val installingDialog = InstallInstallingFragment(installing)
+ showDialogInner(installingDialog)
+ }
+
+ InstallStage.STAGE_SUCCESS -> {
+ val success = installStage as InstallSuccess
+ if (success.shouldReturnResult) {
+ val successIntent = success.resultIntent
+ setResult(Activity.RESULT_OK, successIntent, true)
+ } else {
+ val successFragment = InstallSuccessFragment(success)
+ showDialogInner(successFragment)
+ }
+ }
+
+ InstallStage.STAGE_FAILED -> {
+ val failed = installStage as InstallFailed
+ val failedDialog = InstallFailedFragment(failed)
+ showDialogInner(failedDialog)
+ }
+
+ else -> {
+ Log.d(LOG_TAG, "Unimplemented stage: " + installStage.stageCode)
+ showDialogInner(null)
+ }
+ }
+ }
+
+ private fun showPolicyRestrictionDialog(aborted: InstallAborted) {
+ val restriction = aborted.message
+ val adminSupportIntent = aborted.resultIntent
+ var shouldFinish: Boolean = false
+
+ // If the given restriction is set by an admin, display information about the
+ // admin enforcing the restriction for the affected user. If not enforced by the admin,
+ // show the system dialog.
+ if (adminSupportIntent != null) {
+ if (localLOGV) {
+ Log.i(LOG_TAG, "Restriction set by admin, starting $adminSupportIntent")
+ }
+ startActivity(adminSupportIntent)
+ // Finish the package installer app since the next dialog will not be shown by this app
+ shouldFinish = true
+ } else {
+ if (localLOGV) {
+ Log.i(LOG_TAG, "Restriction set by system: $restriction")
+ }
+ val blockedByPolicyDialog = createDevicePolicyRestrictionDialog(restriction)
+ // Don't finish the package installer app since the next dialog
+ // will be shown by this app
+ shouldFinish = blockedByPolicyDialog != null
+ showDialogInner(blockedByPolicyDialog)
+ }
+ setResult(Activity.RESULT_CANCELED, null, shouldFinish)
+ }
+
+ /**
+ * Create a new dialog based on the install restriction enforced.
+ *
+ * @param restriction The restriction to create the dialog for
+ * @return The dialog
+ */
+ private fun createDevicePolicyRestrictionDialog(restriction: String?): DialogFragment? {
+ if (localLOGV) {
+ Log.i(LOG_TAG, "createDialog($restriction)")
+ }
+ return when (restriction) {
+ UserManager.DISALLOW_INSTALL_APPS ->
+ SimpleErrorFragment(R.string.install_apps_user_restriction_dlg_text)
+
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY ->
+ SimpleErrorFragment(R.string.unknown_apps_user_restriction_dlg_text)
+
+ else -> null
+ }
+ }
+
+ /**
+ * Replace any visible dialog by the dialog returned by InstallRepository
+ *
+ * @param newDialog The new dialog to display
+ */
+ private fun showDialogInner(newDialog: DialogFragment?) {
+ val currentDialog = fragmentManager!!.findFragmentByTag(TAG_DIALOG) as DialogFragment?
+ currentDialog?.dismissAllowingStateLoss()
+ newDialog?.show(fragmentManager!!, TAG_DIALOG)
+ }
+
+ fun setResult(resultCode: Int, data: Intent?, shouldFinish: Boolean) {
+ super.setResult(resultCode, data)
+ if (shouldFinish) {
+ finish()
+ }
+ }
+
+ override fun onPositiveResponse(reasonCode: Int) {
+ when (reasonCode) {
+ InstallUserActionRequired.USER_ACTION_REASON_ANONYMOUS_SOURCE ->
+ installViewModel!!.forcedSkipSourceCheck()
+
+ InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION ->
+ installViewModel!!.initiateInstall()
+ }
+ }
+
+ override fun onNegativeResponse(stageCode: Int) {
+ if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
+ installViewModel!!.cleanupInstall()
+ }
+ setResult(Activity.RESULT_CANCELED, null, true)
+ }
+
+ override fun sendUnknownAppsIntent(sourcePackageName: String) {
+ val settingsIntent = Intent()
+ settingsIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
+ val packageUri = Uri.parse("package:$sourcePackageName")
+ settingsIntent.setData(packageUri)
+ settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
+ try {
+ registerAppOpChangeListener(
+ UnknownSourcesListener(sourcePackageName), sourcePackageName
+ )
+ unknownAppsIntentLauncher.launch(settingsIntent)
+ } catch (exc: ActivityNotFoundException) {
+ Log.e(
+ LOG_TAG, "Settings activity not found for action: "
+ + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES
+ )
+ }
+ }
+
+ override fun openInstalledApp(intent: Intent?) {
+ setResult(Activity.RESULT_OK, intent, true)
+ if (intent != null && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
+ startActivity(intent)
+ }
+ }
+
+ private fun registerAppOpChangeListener(listener: UnknownSourcesListener, packageName: String) {
+ appOpsManager!!.startWatchingMode(
+ AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES,
+ packageName,
+ listener
+ )
+ activeUnknownSourcesListeners.add(listener)
+ }
+
+ private fun unregisterAppOpChangeListener(listener: UnknownSourcesListener) {
+ activeUnknownSourcesListeners.remove(listener)
+ appOpsManager!!.stopWatchingMode(listener)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ while (activeUnknownSourcesListeners.isNotEmpty()) {
+ unregisterAppOpChangeListener(activeUnknownSourcesListeners[0])
+ }
+ }
+
+ private inner class UnknownSourcesListener(private val mOriginatingPackage: String) :
+ AppOpsManager.OnOpChangedListener {
+ override fun onOpChanged(op: String, packageName: String) {
+ if (mOriginatingPackage != packageName) {
+ return
+ }
+ unregisterAppOpChangeListener(this)
+ activeUnknownSourcesListeners.remove(this)
+ if (isDestroyed) {
+ return
+ }
+ Handler(Looper.getMainLooper()).postDelayed({
+ if (!isDestroyed) {
+ // Relaunch Pia to continue installation.
+ startActivity(
+ intent.putExtra(
+ InstallRepository.EXTRA_STAGED_SESSION_ID,
+ installViewModel!!.stagedSessionId
+ )
+ )
+
+ // If the userId of the root of activity stack is different from current userId,
+ // starting Pia again lead to duplicate instances of the app in the stack.
+ // As such, finish the old instance. Old Pia is finished even if the userId of
+ // the root is the same, since there is no way to determine the difference in
+ // userIds.
+ finish()
+ }
+ }, 500)
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.kt
similarity index 78%
rename from packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
rename to packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.kt
index b8a9355..33f5db3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallActionListener.kt
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package com.android.packageinstaller.v2.ui;
+package com.android.packageinstaller.v2.ui
-public interface UninstallActionListener {
-
- void onPositiveResponse(boolean keepData);
-
- void onNegativeResponse();
+interface UninstallActionListener {
+ fun onPositiveResponse(keepData: Boolean)
+ fun onNegativeResponse()
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java
deleted file mode 100644
index 7638e91..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java
+++ /dev/null
@@ -1,167 +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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.ui;
-
-import static android.os.Process.INVALID_UID;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
-import android.app.Activity;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.Toast;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.v2.model.UninstallRepository;
-import com.android.packageinstaller.v2.model.UninstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallFailed;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallSuccess;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
-import com.android.packageinstaller.v2.ui.fragments.UninstallConfirmationFragment;
-import com.android.packageinstaller.v2.ui.fragments.UninstallErrorFragment;
-import com.android.packageinstaller.v2.ui.fragments.UninstallUninstallingFragment;
-import com.android.packageinstaller.v2.viewmodel.UninstallViewModel;
-import com.android.packageinstaller.v2.viewmodel.UninstallViewModelFactory;
-
-public class UninstallLaunch extends FragmentActivity implements UninstallActionListener {
-
- public static final String EXTRA_CALLING_PKG_UID =
- UninstallLaunch.class.getPackageName() + ".callingPkgUid";
- public static final String EXTRA_CALLING_ACTIVITY_NAME =
- UninstallLaunch.class.getPackageName() + ".callingActivityName";
- public static final String TAG = UninstallLaunch.class.getSimpleName();
- private static final String TAG_DIALOG = "dialog";
-
- private UninstallViewModel mUninstallViewModel;
- private UninstallRepository mUninstallRepository;
- private FragmentManager mFragmentManager;
- private NotificationManager mNotificationManager;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
- // Never restore any state, esp. never create any fragments. The data in the fragment might
- // be stale, if e.g. the app was uninstalled while the activity was destroyed.
- super.onCreate(null);
-
- mFragmentManager = getSupportFragmentManager();
- mNotificationManager = getSystemService(NotificationManager.class);
-
- mUninstallRepository = new UninstallRepository(getApplicationContext());
- mUninstallViewModel = new ViewModelProvider(this,
- new UninstallViewModelFactory(this.getApplication(), mUninstallRepository)).get(
- UninstallViewModel.class);
-
- Intent intent = getIntent();
- CallerInfo callerInfo = new CallerInfo(
- intent.getStringExtra(EXTRA_CALLING_ACTIVITY_NAME),
- intent.getIntExtra(EXTRA_CALLING_PKG_UID, INVALID_UID));
- mUninstallViewModel.preprocessIntent(intent, callerInfo);
-
- mUninstallViewModel.getCurrentUninstallStage().observe(this,
- this::onUninstallStageChange);
- }
-
- /**
- * Main controller of the UI. This method shows relevant dialogs / fragments based on the
- * uninstall stage
- */
- private void onUninstallStageChange(UninstallStage uninstallStage) {
- if (uninstallStage.getStageCode() == UninstallStage.STAGE_ABORTED) {
- UninstallAborted aborted = (UninstallAborted) uninstallStage;
- if (aborted.getAbortReason() == UninstallAborted.ABORT_REASON_APP_UNAVAILABLE ||
- aborted.getAbortReason() == UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED) {
- UninstallErrorFragment errorDialog = new UninstallErrorFragment(aborted);
- showDialogInner(errorDialog);
- } else {
- setResult(aborted.getActivityResultCode(), null, true);
- }
- } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_USER_ACTION_REQUIRED) {
- UninstallUserActionRequired uar = (UninstallUserActionRequired) uninstallStage;
- UninstallConfirmationFragment confirmationDialog = new UninstallConfirmationFragment(
- uar);
- showDialogInner(confirmationDialog);
- } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_UNINSTALLING) {
- // TODO: This shows a fragment whether or not user requests a result or not.
- // Originally, if the user does not request a result, we used to show a notification.
- // And a fragment if the user requests a result back. Should we consolidate and
- // show a fragment always?
- UninstallUninstalling uninstalling = (UninstallUninstalling) uninstallStage;
- UninstallUninstallingFragment uninstallingDialog = new UninstallUninstallingFragment(
- uninstalling);
- showDialogInner(uninstallingDialog);
- } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_FAILED) {
- UninstallFailed failed = (UninstallFailed) uninstallStage;
- if (!failed.returnResult()) {
- mNotificationManager.notify(failed.getUninstallId(),
- failed.getUninstallNotification());
- }
- setResult(failed.getActivityResultCode(), failed.getResultIntent(), true);
- } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_SUCCESS) {
- UninstallSuccess success = (UninstallSuccess) uninstallStage;
- if (success.getMessage() != null) {
- Toast.makeText(this, success.getMessage(), Toast.LENGTH_LONG).show();
- }
- setResult(success.getActivityResultCode(), success.getResultIntent(), true);
- } else {
- Log.e(TAG, "Invalid stage: " + uninstallStage.getStageCode());
- showDialogInner(null);
- }
- }
-
- /**
- * Replace any visible dialog by the dialog returned by InstallRepository
- *
- * @param newDialog The new dialog to display
- */
- private void showDialogInner(DialogFragment newDialog) {
- DialogFragment currentDialog = (DialogFragment) mFragmentManager.findFragmentByTag(
- TAG_DIALOG);
- if (currentDialog != null) {
- currentDialog.dismissAllowingStateLoss();
- }
- if (newDialog != null) {
- newDialog.show(mFragmentManager, TAG_DIALOG);
- }
- }
-
- public void setResult(int resultCode, Intent data, boolean shouldFinish) {
- super.setResult(resultCode, data);
- if (shouldFinish) {
- finish();
- }
- }
-
- @Override
- public void onPositiveResponse(boolean keepData) {
- mUninstallViewModel.initiateUninstall(keepData);
- }
-
- @Override
- public void onNegativeResponse() {
- mUninstallViewModel.cancelInstall();
- setResult(Activity.RESULT_FIRST_USER, null, true);
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt
new file mode 100644
index 0000000..0050c7e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.kt
@@ -0,0 +1,169 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.packageinstaller.v2.ui
+
+import android.app.Activity
+import android.app.NotificationManager
+import android.content.Intent
+import android.os.Bundle
+import android.os.Process
+import android.util.Log
+import android.view.WindowManager
+import android.widget.Toast
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.v2.model.UninstallAborted
+import com.android.packageinstaller.v2.model.UninstallFailed
+import com.android.packageinstaller.v2.model.UninstallRepository
+import com.android.packageinstaller.v2.model.UninstallStage
+import com.android.packageinstaller.v2.model.UninstallSuccess
+import com.android.packageinstaller.v2.model.UninstallUninstalling
+import com.android.packageinstaller.v2.model.UninstallUserActionRequired
+import com.android.packageinstaller.v2.ui.fragments.UninstallConfirmationFragment
+import com.android.packageinstaller.v2.ui.fragments.UninstallErrorFragment
+import com.android.packageinstaller.v2.ui.fragments.UninstallUninstallingFragment
+import com.android.packageinstaller.v2.viewmodel.UninstallViewModel
+import com.android.packageinstaller.v2.viewmodel.UninstallViewModelFactory
+
+class UninstallLaunch : FragmentActivity(), UninstallActionListener {
+
+ companion object {
+ @JvmField val EXTRA_CALLING_PKG_UID =
+ UninstallLaunch::class.java.packageName + ".callingPkgUid"
+ @JvmField val EXTRA_CALLING_ACTIVITY_NAME =
+ UninstallLaunch::class.java.packageName + ".callingActivityName"
+ val LOG_TAG = UninstallLaunch::class.java.simpleName
+ private const val TAG_DIALOG = "dialog"
+ }
+
+ private var uninstallViewModel: UninstallViewModel? = null
+ private var uninstallRepository: UninstallRepository? = null
+ private var fragmentManager: FragmentManager? = null
+ private var notificationManager: NotificationManager? = null
+ override fun onCreate(savedInstanceState: Bundle?) {
+ window.addSystemFlags(WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+
+ // Never restore any state, esp. never create any fragments. The data in the fragment might
+ // be stale, if e.g. the app was uninstalled while the activity was destroyed.
+ super.onCreate(null)
+ fragmentManager = supportFragmentManager
+ notificationManager = getSystemService(NotificationManager::class.java)
+
+ uninstallRepository = UninstallRepository(applicationContext)
+ uninstallViewModel = ViewModelProvider(
+ this, UninstallViewModelFactory(this.application, uninstallRepository!!)
+ ).get(UninstallViewModel::class.java)
+
+ val intent = intent
+ val callerInfo = UninstallRepository.CallerInfo(
+ intent.getStringExtra(EXTRA_CALLING_ACTIVITY_NAME),
+ intent.getIntExtra(EXTRA_CALLING_PKG_UID, Process.INVALID_UID)
+ )
+ uninstallViewModel!!.preprocessIntent(intent, callerInfo)
+ uninstallViewModel!!.currentUninstallStage.observe(this) { uninstallStage: UninstallStage ->
+ onUninstallStageChange(uninstallStage)
+ }
+ }
+
+ /**
+ * Main controller of the UI. This method shows relevant dialogs / fragments based on the
+ * uninstall stage
+ */
+ private fun onUninstallStageChange(uninstallStage: UninstallStage) {
+ when (uninstallStage.stageCode) {
+ UninstallStage.STAGE_ABORTED -> {
+ val aborted = uninstallStage as UninstallAborted
+ if (aborted.abortReason == UninstallAborted.ABORT_REASON_APP_UNAVAILABLE ||
+ aborted.abortReason == UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED
+ ) {
+ val errorDialog = UninstallErrorFragment(aborted)
+ showDialogInner(errorDialog)
+ } else {
+ setResult(aborted.activityResultCode, null, true)
+ }
+ }
+
+ UninstallStage.STAGE_USER_ACTION_REQUIRED -> {
+ val uar = uninstallStage as UninstallUserActionRequired
+ val confirmationDialog = UninstallConfirmationFragment(uar)
+ showDialogInner(confirmationDialog)
+ }
+
+ UninstallStage.STAGE_UNINSTALLING -> {
+ // TODO: This shows a fragment whether or not user requests a result or not.
+ // Originally, if the user does not request a result, we used to show a notification.
+ // And a fragment if the user requests a result back. Should we consolidate and
+ // show a fragment always?
+ val uninstalling = uninstallStage as UninstallUninstalling
+ val uninstallingDialog = UninstallUninstallingFragment(uninstalling)
+ showDialogInner(uninstallingDialog)
+ }
+
+ UninstallStage.STAGE_FAILED -> {
+ val failed = uninstallStage as UninstallFailed
+ if (!failed.returnResult) {
+ notificationManager!!.notify(
+ failed.uninstallNotificationId!!, failed.uninstallNotification
+ )
+ }
+ setResult(failed.activityResultCode, failed.resultIntent, true)
+ }
+
+ UninstallStage.STAGE_SUCCESS -> {
+ val success = uninstallStage as UninstallSuccess
+ if (success.message != null) {
+ Toast.makeText(this, success.message, Toast.LENGTH_LONG).show()
+ }
+ setResult(success.activityResultCode, success.resultIntent, true)
+ }
+
+ else -> {
+ Log.e(LOG_TAG, "Invalid stage: " + uninstallStage.stageCode)
+ showDialogInner(null)
+ }
+ }
+ }
+
+ /**
+ * Replace any visible dialog by the dialog returned by InstallRepository
+ *
+ * @param newDialog The new dialog to display
+ */
+ private fun showDialogInner(newDialog: DialogFragment?) {
+ val currentDialog = fragmentManager!!.findFragmentByTag(TAG_DIALOG) as DialogFragment?
+ currentDialog?.dismissAllowingStateLoss()
+ newDialog?.show(fragmentManager!!, TAG_DIALOG)
+ }
+
+ fun setResult(resultCode: Int, data: Intent?, shouldFinish: Boolean) {
+ super.setResult(resultCode, data)
+ if (shouldFinish) {
+ finish()
+ }
+ }
+
+ override fun onPositiveResponse(keepData: Boolean) {
+ uninstallViewModel!!.initiateUninstall(keepData)
+ }
+
+ override fun onNegativeResponse() {
+ uninstallViewModel!!.cancelInstall()
+ setResult(Activity.RESULT_FIRST_USER, null, true)
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
index 6d6fcc9..679f696 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/AnonymousSourceFragment.java
@@ -24,8 +24,8 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.model.InstallStage;
+import com.android.packageinstaller.v2.model.InstallUserActionRequired;
import com.android.packageinstaller.v2.ui.InstallActionListener;
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
index 4cdce52..49901de 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/ExternalSourcesBlockedFragment.java
@@ -25,7 +25,7 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.model.InstallUserActionRequired;
import com.android.packageinstaller.v2.ui.InstallActionListener;
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index 6398aef..25363d0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -28,7 +28,7 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallUserActionRequired;
+import com.android.packageinstaller.v2.model.InstallUserActionRequired;
import com.android.packageinstaller.v2.ui.InstallActionListener;
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
index d45cd76..4667a7a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallFailedFragment.java
@@ -28,7 +28,7 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallFailed;
+import com.android.packageinstaller.v2.model.InstallFailed;
import com.android.packageinstaller.v2.ui.InstallActionListener;
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
index 9f60f96..7327b5d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallInstallingFragment.java
@@ -25,7 +25,7 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallInstalling;
+import com.android.packageinstaller.v2.model.InstallInstalling;
/**
* Dialog to show when an install is in progress.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
index ab6a932..b2a65faa 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallSuccessFragment.java
@@ -29,8 +29,8 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallSuccess;
+import com.android.packageinstaller.v2.model.InstallStage;
+import com.android.packageinstaller.v2.model.InstallSuccess;
import com.android.packageinstaller.v2.ui.InstallActionListener;
import java.util.List;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
index 47fd67f..58b8b2d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/SimpleErrorFragment.java
@@ -24,7 +24,7 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
+import com.android.packageinstaller.v2.model.InstallStage;
import com.android.packageinstaller.v2.ui.InstallActionListener;
public class SimpleErrorFragment extends DialogFragment {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
index 1b0885e..32ac4a6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
@@ -30,7 +30,7 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
+import com.android.packageinstaller.v2.model.UninstallUserActionRequired;
import com.android.packageinstaller.v2.ui.UninstallActionListener;
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
index 305daba..eb7183d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallErrorFragment.java
@@ -25,7 +25,7 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
+import com.android.packageinstaller.v2.model.UninstallAborted;
import com.android.packageinstaller.v2.ui.UninstallActionListener;
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
index 23cc421..835efc6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallUninstallingFragment.java
@@ -22,7 +22,7 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.android.packageinstaller.R;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
+import com.android.packageinstaller.v2.model.UninstallUninstalling;
/**
* Dialog to show that the app is uninstalling.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java
deleted file mode 100644
index 04a0622..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.java
+++ /dev/null
@@ -1,105 +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.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.MediatorLiveData;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.v2.model.InstallRepository;
-import com.android.packageinstaller.v2.model.InstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStage;
-import com.android.packageinstaller.v2.model.installstagedata.InstallStaging;
-
-
-public class InstallViewModel extends AndroidViewModel {
-
- private static final String TAG = InstallViewModel.class.getSimpleName();
- private final InstallRepository mRepository;
- private final MediatorLiveData<InstallStage> mCurrentInstallStage = new MediatorLiveData<>(
- new InstallStaging());
-
- public InstallViewModel(@NonNull Application application, InstallRepository repository) {
- super(application);
- mRepository = repository;
- }
-
- public MutableLiveData<InstallStage> getCurrentInstallStage() {
- return mCurrentInstallStage;
- }
-
- public void preprocessIntent(Intent intent, CallerInfo callerInfo) {
- InstallStage stage = mRepository.performPreInstallChecks(intent, callerInfo);
- if (stage.getStageCode() == InstallStage.STAGE_ABORTED) {
- mCurrentInstallStage.setValue(stage);
- } else {
- // Since staging is an async operation, we will get the staging result later in time.
- // Result of the file staging will be set in InstallRepository#mStagingResult.
- // As such, mCurrentInstallStage will need to add another MutableLiveData
- // as a data source
- mRepository.stageForInstall();
- mCurrentInstallStage.addSource(mRepository.getStagingResult(), installStage -> {
- if (installStage.getStageCode() != InstallStage.STAGE_READY) {
- mCurrentInstallStage.setValue(installStage);
- } else {
- checkIfAllowedAndInitiateInstall();
- }
- });
- }
- }
-
- public MutableLiveData<Integer> getStagingProgress() {
- return mRepository.getStagingProgress();
- }
-
- private void checkIfAllowedAndInitiateInstall() {
- InstallStage stage = mRepository.requestUserConfirmation();
- mCurrentInstallStage.setValue(stage);
- }
-
- public void forcedSkipSourceCheck() {
- InstallStage stage = mRepository.forcedSkipSourceCheck();
- mCurrentInstallStage.setValue(stage);
- }
-
- public void cleanupInstall() {
- mRepository.cleanupInstall();
- }
-
- public void reattemptInstall() {
- InstallStage stage = mRepository.reattemptInstall();
- mCurrentInstallStage.setValue(stage);
- }
-
- public void initiateInstall() {
- // Since installing is an async operation, we will get the install result later in time.
- // Result of the installation will be set in InstallRepository#mInstallResult.
- // As such, mCurrentInstallStage will need to add another MutableLiveData as a data source
- mRepository.initiateInstall();
- mCurrentInstallStage.addSource(mRepository.getInstallResult(), installStage -> {
- if (installStage != null) {
- mCurrentInstallStage.setValue(installStage);
- }
- });
- }
-
- public int getStagedSessionId() {
- return mRepository.getStagedSessionId();
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt
new file mode 100644
index 0000000..072fb2d
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import android.content.Intent
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.v2.model.InstallRepository
+import com.android.packageinstaller.v2.model.InstallStage
+import com.android.packageinstaller.v2.model.InstallStaging
+
+class InstallViewModel(application: Application, val repository: InstallRepository) :
+ AndroidViewModel(application) {
+
+ companion object {
+ private val LOG_TAG = InstallViewModel::class.java.simpleName
+ }
+
+ private val _currentInstallStage = MediatorLiveData<InstallStage>(InstallStaging())
+ val currentInstallStage: MutableLiveData<InstallStage>
+ get() = _currentInstallStage
+
+ fun preprocessIntent(intent: Intent, callerInfo: InstallRepository.CallerInfo) {
+ val stage = repository.performPreInstallChecks(intent, callerInfo)
+ if (stage.stageCode == InstallStage.STAGE_ABORTED) {
+ _currentInstallStage.value = stage
+ } else {
+ // Since staging is an async operation, we will get the staging result later in time.
+ // Result of the file staging will be set in InstallRepository#mStagingResult.
+ // As such, mCurrentInstallStage will need to add another MutableLiveData
+ // as a data source
+ repository.stageForInstall()
+ _currentInstallStage.addSource(repository.stagingResult) { installStage: InstallStage ->
+ if (installStage.stageCode != InstallStage.STAGE_READY) {
+ _currentInstallStage.value = installStage
+ } else {
+ checkIfAllowedAndInitiateInstall()
+ }
+ }
+ }
+ }
+
+ val stagingProgress: LiveData<Int>
+ get() = repository.stagingProgress
+
+ private fun checkIfAllowedAndInitiateInstall() {
+ val stage = repository.requestUserConfirmation()
+ _currentInstallStage.value = stage
+ }
+
+ fun forcedSkipSourceCheck() {
+ val stage = repository.forcedSkipSourceCheck()
+ _currentInstallStage.value = stage
+ }
+
+ fun cleanupInstall() {
+ repository.cleanupInstall()
+ }
+
+ fun reattemptInstall() {
+ val stage = repository.reattemptInstall()
+ _currentInstallStage.value = stage
+ }
+
+ fun initiateInstall() {
+ // Since installing is an async operation, we will get the install result later in time.
+ // Result of the installation will be set in InstallRepository#mInstallResult.
+ // As such, mCurrentInstallStage will need to add another MutableLiveData as a data source
+ repository.initiateInstall()
+ _currentInstallStage.addSource(repository.installResult) { installStage: InstallStage? ->
+ if (installStage != null) {
+ _currentInstallStage.value = installStage
+ }
+ }
+ }
+
+ val stagedSessionId: Int
+ get() = repository.stagedSessionId
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java
deleted file mode 100644
index ef459e6..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.java
+++ /dev/null
@@ -1,45 +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.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.v2.model.InstallRepository;
-
-public class InstallViewModelFactory extends ViewModelProvider.AndroidViewModelFactory {
-
- private final InstallRepository mRepository;
- private final Application mApplication;
-
- public InstallViewModelFactory(Application application, InstallRepository repository) {
- // Calling super class' ctor ensures that create method is called correctly and the right
- // ctor of InstallViewModel is used. If we fail to do that, the default ctor:
- // InstallViewModel(application) is used, and repository isn't initialized in the viewmodel
- super(application);
- mApplication = application;
- mRepository = repository;
- }
-
- @NonNull
- @Override
- @SuppressWarnings("unchecked")
- public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
- return (T) new InstallViewModel(mApplication, mRepository);
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.kt
new file mode 100644
index 0000000..07b2f4f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModelFactory.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.v2.model.InstallRepository
+
+class InstallViewModelFactory(val application: Application, val repository: InstallRepository) :
+ ViewModelProvider.AndroidViewModelFactory(application) {
+
+ // Calling super class' ctor ensures that create method is called correctly and the right
+ // ctor of InstallViewModel is used. If we fail to do that, the default ctor:
+ // InstallViewModel(application) is used, and repository isn't initialized in the viewmodel
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ return InstallViewModel(application, repository) as T
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java
deleted file mode 100644
index 3f7bce8..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java
+++ /dev/null
@@ -1,69 +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
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import android.content.Intent;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.MediatorLiveData;
-import androidx.lifecycle.MutableLiveData;
-import com.android.packageinstaller.v2.model.UninstallRepository;
-import com.android.packageinstaller.v2.model.UninstallRepository.CallerInfo;
-import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
-
-public class UninstallViewModel extends AndroidViewModel {
-
- private static final String TAG = UninstallViewModel.class.getSimpleName();
- private final UninstallRepository mRepository;
- private final MediatorLiveData<UninstallStage> mCurrentUninstallStage =
- new MediatorLiveData<>();
-
- public UninstallViewModel(@NonNull Application application, UninstallRepository repository) {
- super(application);
- mRepository = repository;
- }
-
- public MutableLiveData<UninstallStage> getCurrentUninstallStage() {
- return mCurrentUninstallStage;
- }
-
- public void preprocessIntent(Intent intent, CallerInfo callerInfo) {
- UninstallStage stage = mRepository.performPreUninstallChecks(intent, callerInfo);
- if (stage.getStageCode() != UninstallStage.STAGE_ABORTED) {
- stage = mRepository.generateUninstallDetails();
- }
- mCurrentUninstallStage.setValue(stage);
- }
-
- public void initiateUninstall(boolean keepData) {
- mRepository.initiateUninstall(keepData);
- // Since uninstall is an async operation, we will get the uninstall result later in time.
- // Result of the uninstall will be set in UninstallRepository#mUninstallResult.
- // As such, mCurrentUninstallStage will need to add another MutableLiveData
- // as a data source
- mCurrentUninstallStage.addSource(mRepository.getUninstallResult(), uninstallStage -> {
- if (uninstallStage != null) {
- mCurrentUninstallStage.setValue(uninstallStage);
- }
- });
- }
-
- public void cancelInstall() {
- mRepository.cancelInstall();
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.kt
new file mode 100644
index 0000000..80886e9
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import android.content.Intent
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
+import com.android.packageinstaller.v2.model.UninstallRepository
+import com.android.packageinstaller.v2.model.UninstallStage
+
+class UninstallViewModel(application: Application, val repository: UninstallRepository) :
+ AndroidViewModel(application) {
+
+ companion object {
+ private val LOG_TAG = UninstallViewModel::class.java.simpleName
+ }
+
+ private val _currentUninstallStage = MediatorLiveData<UninstallStage>()
+ val currentUninstallStage: MutableLiveData<UninstallStage>
+ get() = _currentUninstallStage
+
+ fun preprocessIntent(intent: Intent, callerInfo: UninstallRepository.CallerInfo) {
+ var stage = repository.performPreUninstallChecks(intent, callerInfo)
+ if (stage.stageCode != UninstallStage.STAGE_ABORTED) {
+ stage = repository.generateUninstallDetails()
+ }
+ _currentUninstallStage.value = stage
+ }
+
+ fun initiateUninstall(keepData: Boolean) {
+ repository.initiateUninstall(keepData)
+ // Since uninstall is an async operation, we will get the uninstall result later in time.
+ // Result of the uninstall will be set in UninstallRepository#mUninstallResult.
+ // As such, _currentUninstallStage will need to add another MutableLiveData
+ // as a data source
+ _currentUninstallStage.addSource(repository.uninstallResult) { uninstallStage: UninstallStage? ->
+ if (uninstallStage != null) {
+ _currentUninstallStage.value = uninstallStage
+ }
+ }
+ }
+
+ fun cancelInstall() {
+ repository.cancelInstall()
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.java
deleted file mode 100644
index cd9845e..0000000
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.java
+++ /dev/null
@@ -1,46 +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.packageinstaller.v2.viewmodel;
-
-import android.app.Application;
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-import com.android.packageinstaller.v2.model.UninstallRepository;
-
-public class UninstallViewModelFactory extends ViewModelProvider.AndroidViewModelFactory {
-
- private final UninstallRepository mRepository;
- private final Application mApplication;
-
- public UninstallViewModelFactory(Application application, UninstallRepository repository) {
- // Calling super class' ctor ensures that create method is called correctly and the right
- // ctor of UninstallViewModel is used. If we fail to do that, the default ctor:
- // UninstallViewModel(application) is used, and repository isn't initialized in
- // the viewmodel
- super(application);
- mApplication = application;
- mRepository = repository;
- }
-
- @NonNull
- @Override
- @SuppressWarnings("unchecked")
- public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
- return (T) new UninstallViewModel(mApplication, mRepository);
- }
-}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.kt
new file mode 100644
index 0000000..0a316e7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModelFactory.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.packageinstaller.v2.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.packageinstaller.v2.model.UninstallRepository
+
+class UninstallViewModelFactory(val application: Application, val repository: UninstallRepository) :
+ ViewModelProvider.AndroidViewModelFactory(application) {
+
+ // Calling super class' ctor ensures that create method is called correctly and the right
+ // ctor of UninstallViewModel is used. If we fail to do that, the default ctor:
+ // UninstallViewModel(application) is used, and repository isn't initialized in
+ // the viewmodel
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ return UninstallViewModel(application, repository) as T
+ }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
index df72a92..044ba87 100644
--- a/packages/SettingsLib/AdaptiveIcon/Android.bp
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -15,9 +15,12 @@
resource_dirs: ["res"],
static_libs: [
- "androidx.annotation_annotation",
- "SettingsLibTile"
+ "androidx.annotation_annotation",
+ "SettingsLibTile",
],
min_sdk_version: "21",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 2501869..ffe3d1d 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -60,6 +60,9 @@
"src/**/*.java",
"src/**/*.kt",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
// NOTE: Keep this module in sync with ./common.mk
diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp
index 986baf7..bd0dbdc 100644
--- a/packages/SettingsLib/EmergencyNumber/Android.bp
+++ b/packages/SettingsLib/EmergencyNumber/Android.bp
@@ -17,4 +17,7 @@
],
sdk_version: "system_current",
min_sdk_version: "21",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 010a6ce..d9f74da 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -28,4 +28,7 @@
"com.android.extservices",
"com.android.healthfitness",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index 028489d..3b04bd9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -30,4 +30,7 @@
"//apex_available:platform",
"com.android.permission",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp
index 2d93e4e..22e4e94 100644
--- a/packages/SettingsLib/SchedulesProvider/Android.bp
+++ b/packages/SettingsLib/SchedulesProvider/Android.bp
@@ -14,9 +14,12 @@
srcs: ["src/**/*.java"],
static_libs: [
- "androidx.annotation_annotation",
+ "androidx.annotation_annotation",
],
sdk_version: "system_current",
min_sdk_version: "21",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/SearchProvider/Android.bp b/packages/SettingsLib/SearchProvider/Android.bp
index c07a802..c385d38 100644
--- a/packages/SettingsLib/SearchProvider/Android.bp
+++ b/packages/SettingsLib/SearchProvider/Android.bp
@@ -15,4 +15,7 @@
sdk_version: "system_current",
min_sdk_version: "21",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index 66bd6f5..d5cf1a35 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -26,6 +26,7 @@
import androidx.compose.material.icons.outlined.PowerOff
import androidx.compose.material.icons.outlined.Shield
import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -78,24 +79,19 @@
imageVector = Icons.Outlined.WarningAmber,
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Action", isMain = true) {},
- )
+ ),
+ tintColor = MaterialTheme.colorScheme.error,
+ containerColor = MaterialTheme.colorScheme.errorContainer,
)
)
}
@Composable
private fun SettingsCardWithoutIcon() {
- var isVisible by rememberSaveable { mutableStateOf(true) }
SettingsCard(
CardModel(
title = stringResource(R.string.sample_title),
text = stringResource(R.string.sample_text),
- isVisible = { isVisible },
- onDismiss = { isVisible = false },
- buttons = listOf(
- CardButton(text = "Action") {},
- ),
)
)
}
@@ -104,6 +100,7 @@
fun SampleSettingsCollapsibleCard() {
val context = LocalContext.current
var isVisible0 by rememberSaveable { mutableStateOf(true) }
+ var isVisible1 by rememberSaveable { mutableStateOf(true) }
val cards = remember {
mutableStateListOf(
CardModel(
@@ -114,16 +111,17 @@
onDismiss = { isVisible0 = false },
buttons = listOf(
CardButton(text = "Action") {},
- )
+ ),
),
CardModel(
title = context.getString(R.string.sample_title),
text = context.getString(R.string.sample_text),
imageVector = Icons.Outlined.Shield,
+ isVisible = { isVisible1 },
+ onDismiss = { isVisible1 = false },
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Main action", isMain = true) {},
- )
+ ),
)
)
}
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 993cb4a..c143390 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
@@ -37,7 +37,6 @@
val itemPaddingAround = 8.dp
val itemDividerHeight = 32.dp
- val iconSmall = 16.dp
val iconLarge = 48.dp
/** The size when app icon is displayed in list. */
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
index b18a1bc..b2a8b87 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -16,11 +16,11 @@
package com.android.settingslib.spa.widget.card
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
data class CardButton(
val text: String,
- val isMain: Boolean = false,
val onClick: () -> Unit,
)
@@ -38,4 +38,10 @@
val onDismiss: (() -> Unit)? = null,
val buttons: List<CardButton> = emptyList(),
+
+ /** If specified, this color will be used to tint the icon and the buttons. */
+ val tintColor: Color = Color.Unspecified,
+
+ /** If specified, this color will be used to tint the icon and the buttons. */
+ val containerColor: Color = Color.Unspecified,
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 7eec888..c7845fa 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -23,26 +23,26 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.WarningAmber
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -72,11 +72,14 @@
}
@Composable
-fun SettingsCardContent(content: @Composable ColumnScope.() -> Unit) {
+fun SettingsCardContent(
+ containerColor: Color = Color.Unspecified,
+ content: @Composable ColumnScope.() -> Unit,
+) {
Card(
shape = CornerExtraSmall,
colors = CardDefaults.cardColors(
- containerColor = SettingsTheme.colorScheme.surface,
+ containerColor = containerColor.takeOrElse { SettingsTheme.colorScheme.surface },
),
modifier = Modifier
.fillMaxWidth()
@@ -95,37 +98,43 @@
@Composable
internal fun SettingsCardImpl(model: CardModel) {
AnimatedVisibility(visible = model.isVisible()) {
- SettingsCardContent {
+ SettingsCardContent(containerColor = model.containerColor) {
Column(
- modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+ modifier = Modifier.padding(
+ horizontal = SettingsDimension.dialogItemPaddingHorizontal,
+ vertical = SettingsDimension.itemPaddingAround,
+ ),
verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
) {
- CardHeader(model.imageVector, model.onDismiss)
+ CardHeader(model.imageVector, model.tintColor, model.onDismiss)
SettingsTitle(model.title)
SettingsBody(model.text)
- Buttons(model.buttons)
+ Buttons(model.buttons, model.tintColor)
}
}
}
}
@Composable
-fun CardHeader(imageVector: ImageVector?, onDismiss: (() -> Unit)? = null) {
+fun CardHeader(imageVector: ImageVector?, iconColor: Color, onDismiss: (() -> Unit)? = null) {
+ if (imageVector != null || onDismiss != null) {
+ Spacer(Modifier.height(SettingsDimension.buttonPaddingVertical))
+ }
Row(Modifier.fillMaxWidth()) {
- CardIcon(imageVector)
+ CardIcon(imageVector, iconColor)
Spacer(modifier = Modifier.weight(1f))
DismissButton(onDismiss)
}
}
@Composable
-private fun CardIcon(imageVector: ImageVector?) {
+private fun CardIcon(imageVector: ImageVector?, color: Color) {
if (imageVector != null) {
Icon(
imageVector = imageVector,
contentDescription = null,
modifier = Modifier.size(SettingsDimension.itemIconSize),
- tint = MaterialTheme.colorScheme.primary,
+ tint = color.takeOrElse { MaterialTheme.colorScheme.primary },
)
}
}
@@ -146,52 +155,35 @@
contentDescription = stringResource(
androidx.compose.material3.R.string.m3c_snackbar_dismiss
),
- modifier = Modifier.size(SettingsDimension.iconSmall),
+ modifier = Modifier.padding(SettingsDimension.paddingSmall),
)
}
}
}
@Composable
-private fun Buttons(buttons: List<CardButton>) {
+private fun Buttons(buttons: List<CardButton>, color: Color) {
if (buttons.isNotEmpty()) {
Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = SettingsDimension.itemPaddingAround),
+ modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(
space = SettingsDimension.itemPaddingEnd,
alignment = Alignment.End,
),
) {
for (button in buttons) {
- Button(button)
+ Button(button, color)
}
}
+ } else {
+ Spacer(Modifier.height(SettingsDimension.itemPaddingAround))
}
}
@Composable
-private fun Button(button: CardButton) {
- if (button.isMain) {
- Button(
- onClick = button.onClick,
- colors = ButtonDefaults.buttonColors(
- containerColor = SettingsTheme.colorScheme.primaryContainer,
- ),
- ) {
- Text(
- text = button.text,
- color = SettingsTheme.colorScheme.onPrimaryContainer,
- )
- }
- } else {
- OutlinedButton(onClick = button.onClick) {
- Text(
- text = button.text,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
+private fun Button(button: CardButton, color: Color) {
+ TextButton(onClick = button.onClick) {
+ Text(text = button.text, color = color)
}
}
@@ -206,7 +198,6 @@
imageVector = Icons.Outlined.WarningAmber,
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Action", isMain = true) {},
)
)
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
index 6e36490..c34df65 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
@@ -141,7 +141,6 @@
imageVector = Icons.Outlined.Shield,
buttons = listOf(
CardButton(text = "Action") {},
- CardButton(text = "Main action", isMain = true) {},
)
)
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/CheckboxPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/CheckboxPreference.kt
new file mode 100644
index 0000000..93d77d4
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/CheckboxPreference.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AirplanemodeActive
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.tooling.preview.Preview
+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.util.EntryHighlight
+import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
+import com.android.settingslib.spa.widget.ui.SettingsCheckbox
+import com.android.settingslib.spa.widget.ui.SettingsIcon
+
+/**
+ * The widget model for [CheckboxPreference] widget.
+ */
+interface CheckboxPreferenceModel {
+ /**
+ * The title of this [CheckboxPreference].
+ */
+ val title: String
+
+ /**
+ * The summary of this [CheckboxPreference].
+ */
+ val summary: () -> String
+ get() = { "" }
+
+ /**
+ * The icon of this [Preference].
+ *
+ * Default is `null` which means no icon.
+ */
+ val icon: (@Composable () -> Unit)?
+ get() = null
+
+ /**
+ * Indicates whether this [CheckboxPreference] is checked.
+ *
+ * This can be `null` during the data loading before the data is available.
+ */
+ val checked: () -> Boolean?
+
+ /**
+ * Indicates whether this [CheckboxPreference] is changeable.
+ *
+ * Not changeable [CheckboxPreference] will be displayed in disabled style.
+ */
+ val changeable: () -> Boolean
+ get() = { true }
+
+ /**
+ * The checkbox change handler of this [CheckboxPreference].
+ *
+ * If `null`, this [CheckboxPreference] is not [toggleable].
+ */
+ val onCheckedChange: ((newChecked: Boolean) -> Unit)?
+}
+
+/**
+ * CheckboxPreference widget.
+ *
+ * Data is provided through [CheckboxPreferenceModel].
+ */
+@Composable
+fun CheckboxPreference(model: CheckboxPreferenceModel) {
+ EntryHighlight {
+ InternalCheckboxPreference(
+ title = model.title,
+ summary = model.summary,
+ icon = model.icon,
+ checked = model.checked(),
+ changeable = model.changeable(),
+ onCheckedChange = model.onCheckedChange,
+ )
+ }
+}
+
+@Composable
+internal fun InternalCheckboxPreference(
+ title: String,
+ summary: () -> String = { "" },
+ icon: @Composable (() -> Unit)? = null,
+ checked: Boolean?,
+ changeable: Boolean = true,
+ paddingStart: Dp = SettingsDimension.itemPaddingStart,
+ paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+ paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+ onCheckedChange: ((newChecked: Boolean) -> Unit)?,
+) {
+ val indication = LocalIndication.current
+ val onChangeWithLog = wrapOnSwitchWithLog(onCheckedChange)
+ val interactionSource = remember { MutableInteractionSource() }
+ val modifier = remember(checked, changeable) {
+ if (checked != null && onChangeWithLog != null) {
+ Modifier.toggleable(
+ value = checked,
+ interactionSource = interactionSource,
+ indication = indication,
+ enabled = changeable,
+ role = Role.Checkbox,
+ onValueChange = onChangeWithLog,
+ )
+ } else Modifier
+ }
+ BasePreference(
+ title = title,
+ summary = summary,
+ modifier = modifier,
+ enabled = { changeable },
+ paddingStart = paddingStart,
+ paddingEnd = paddingEnd,
+ paddingVertical = paddingVertical,
+ icon = icon,
+ ) {
+ Spacer(Modifier.width(SettingsDimension.itemPaddingEnd))
+ SettingsCheckbox(
+ checked = checked,
+ changeable = { changeable },
+ // The onCheckedChange is handled on the whole CheckboxPreference.
+ // DO NOT set it on SettingsCheckbox.
+ onCheckedChange = null,
+ interactionSource = interactionSource,
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun CheckboxPreferencePreview() {
+ SettingsTheme {
+ Column {
+ InternalCheckboxPreference(
+ title = "Use Dark theme",
+ checked = true,
+ onCheckedChange = {},
+ )
+ InternalCheckboxPreference(
+ title = "Use Dark theme",
+ summary = { "Summary" },
+ checked = false,
+ onCheckedChange = {},
+ )
+ InternalCheckboxPreference(
+ title = "Use Dark theme",
+ summary = { "Summary" },
+ checked = true,
+ onCheckedChange = {},
+ icon = @Composable {
+ SettingsIcon(imageVector = Icons.Outlined.AirplanemodeActive)
+ },
+ )
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Checkbox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Checkbox.kt
new file mode 100644
index 0000000..7a9d46c
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Checkbox.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.settingslib.spa.widget.ui
+
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.material3.Checkbox
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
+
+@Composable
+internal fun SettingsCheckbox(
+ checked: Boolean?,
+ changeable: () -> Boolean,
+ onCheckedChange: ((newChecked: Boolean) -> Unit)? = null,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) {
+ if (checked != null) {
+ Checkbox(
+ checked = checked,
+ onCheckedChange = wrapOnSwitchWithLog(onCheckedChange),
+ enabled = changeable(),
+ interactionSource = interactionSource,
+ )
+ } else {
+ Checkbox(
+ checked = false,
+ onCheckedChange = null,
+ enabled = false,
+ interactionSource = interactionSource,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/CheckboxPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/CheckboxPreferenceTest.kt
new file mode 100644
index 0000000..7375923
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/CheckboxPreferenceTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.spa.widget.preference
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class CheckboxPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ testCheckboxPreference(changeable = true)
+ }
+
+ composeTestRule.onNodeWithText("CheckboxPreference").assertIsDisplayed()
+ }
+
+ @Test
+ fun toggleable_initialStateIsCorrect() {
+ composeTestRule.setContent {
+ testCheckboxPreference(changeable = true)
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+
+ @Test
+ fun click_changeable_withEffect() {
+ composeTestRule.setContent {
+ testCheckboxPreference(changeable = true)
+ }
+
+ composeTestRule.onNodeWithText("CheckboxPreference").performClick()
+ composeTestRule.onNode(isToggleable()).assertIsOn()
+ }
+
+ @Test
+ fun click_notChangeable_noEffect() {
+ composeTestRule.setContent {
+ testCheckboxPreference(changeable = false)
+ }
+
+ composeTestRule.onNodeWithText("CheckboxPreference").performClick()
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+}
+
+@Composable
+private fun testCheckboxPreference(changeable: Boolean) {
+ var checked by rememberSaveable { mutableStateOf(false) }
+ CheckboxPreference(remember {
+ object : CheckboxPreferenceModel {
+ override val title = "CheckboxPreference"
+ override val checked = { checked }
+ override val changeable = { changeable }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
+ }
+ })
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
index eb9e329..19c59dd 100644
--- a/packages/SettingsLib/Tile/Android.bp
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -14,8 +14,11 @@
srcs: ["src/**/*.java"],
static_libs: [
- "androidx.annotation_annotation",
+ "androidx.annotation_annotation",
],
min_sdk_version: "21",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp
index c7ad24c..d5a56c8 100644
--- a/packages/SettingsLib/Utils/Android.bp
+++ b/packages/SettingsLib/Utils/Android.bp
@@ -31,4 +31,7 @@
"com.android.healthfitness",
"com.android.permission",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 9d783ec..3c5135b 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -26,7 +26,7 @@
<item msgid="6050951078202663628">"Opretter forbindelse..."</item>
<item msgid="8356618438494652335">"Godkender..."</item>
<item msgid="2837871868181677206">"Henter IP-adresse…"</item>
- <item msgid="4613015005934755724">"Tilsluttet"</item>
+ <item msgid="4613015005934755724">"Forbundet"</item>
<item msgid="3763530049995655072">"Sat på pause"</item>
<item msgid="7852381437933824454">"Afbryder ..."</item>
<item msgid="5046795712175415059">"Afbrudt"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 2a6a247..0d0f0c6 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -85,15 +85,15 @@
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Afbrudt"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Afbryder ..."</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"Opretter forbindelse..."</string>
- <string name="bluetooth_connected" msgid="8065345572198502293">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_connected" msgid="8065345572198502293">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_pairing" msgid="4269046942588193600">"Parrer..."</string>
- <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon)"</string>
- <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen medier)"</string>
- <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon eller medier)"</string>
- <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon eller medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon)"</string>
+ <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen medier)"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon eller medier)"</string>
+ <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Forbundet med <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon eller medier) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktiv, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktivt, V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
@@ -129,7 +129,7 @@
<string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"Sluttet til SAP"</string>
<string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Ikke forbundet til filoverførselsserver"</string>
<string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"Forbundet til inputenhed"</string>
- <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Tilsluttet enhed for at få internetadgang"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Forbundet med enhed for at få internetadgang"</string>
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Deler lokal internetforbindelse med enhed"</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="7422039765025340313">"Brug til internetadgang"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"Brug til kort"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 364ba54..5d2210c 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -155,8 +155,8 @@
<string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Entzungailua"</string>
<string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Idazteko gailua"</string>
<string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth bidezko gailua"</string>
- <string name="accessibility_wifi_off" msgid="1195445715254137155">"Desaktibatuta dago Wi-Fi konexioa."</string>
- <string name="accessibility_no_wifi" msgid="5297119459491085771">"Deskonektatu egin da Wi-Fi konexioa."</string>
+ <string name="accessibility_wifi_off" msgid="1195445715254137155">"Desaktibatuta dago wifi-konexioa."</string>
+ <string name="accessibility_no_wifi" msgid="5297119459491085771">"Deskonektatu egin da wifi-konexioa."</string>
<string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Wi-Fi sarearen barra bat."</string>
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi sarearen bi barra."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi sarearen hiru barra."</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index a5bfe5e..0815268 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -84,7 +84,7 @@
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Frakoblet"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Kobler fra…"</string>
- <string name="bluetooth_connecting" msgid="5871702668260192755">"Kobler til…"</string>
+ <string name="bluetooth_connecting" msgid="5871702668260192755">"Kobler til …"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"Koblet til <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_pairing" msgid="4269046942588193600">"Kobler til …"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Koblet til (ingen telefon) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index d8e1152..6c4ddf2 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -335,7 +335,7 @@
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB मा एपको पुष्टि गरियोस्"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक व्यवहार पत्ता लगाउन ADB/ADT बाट इन्स्टल गरिएका एपको जाँच गरियोस्"</string>
<string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नामकरण नगरिएका ब्लुटुथ डिभाइस (म्याक एड्रेस भएका मात्र) देखाइने छ"</string>
- <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"यसले रिमोट डिभाइसमा अत्याधिक ठूलो वा अनियन्त्रित भोल्युम बज्नेको जस्ता अवस्थामा ब्लुटुथको निरपेक्ष भोल्युम अफ गर्छ।"</string>
+ <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"यसले रिमोट डिभाइसमा अत्यधिक ठूलो वा अनियन्त्रित भोल्युम बज्नेको जस्ता अवस्थामा ब्लुटुथको निरपेक्ष भोल्युम अफ गर्छ।"</string>
<string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ब्लुटुथ Gabeldorsche सुविधाको स्ट्याक अन गरियोस्।"</string>
<string name="enhanced_connectivity_summary" msgid="1576414159820676330">"यसले परिष्कृत जडानको सुविधा सक्षम पार्छ।"</string>
<string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string>
diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp
index 202e7fe..3b14712 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -12,6 +12,9 @@
visibility: ["//visibility:private"],
srcs: ["interface-src/**/*.java"],
host_supported: true,
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
android_library {
@@ -24,6 +27,9 @@
sdk_version: "system_current",
min_sdk_version: "21",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_plugin {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index c51a9a0..8e5396f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -23,8 +23,6 @@
import static com.android.settingslib.Utils.getColorAttrDefaultColor;
import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -52,6 +50,8 @@
import android.view.MenuItem;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 3b8f665..70ece0f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -21,7 +21,6 @@
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
@@ -35,6 +34,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 107d5f8..c2be571 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -3,7 +3,6 @@
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
import android.annotation.ColorInt;
-import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -23,10 +22,10 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
-import android.hardware.usb.flags.Flags;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.flags.Flags;
import android.location.LocationManager;
import android.media.AudioManager;
import android.net.NetworkCapabilities;
@@ -47,6 +46,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
index dae48db..b0db16f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -17,7 +17,6 @@
package com.android.settingslib.applications;
import android.app.AppGlobals;
-import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -28,6 +27,8 @@
import android.os.RemoteException;
import android.util.IconDrawableFactory;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.widget.CandidateInfo;
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index fa056e2b..416b369 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -26,9 +26,9 @@
import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
index 1900575..cf4d6be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HapClientProfile.java
@@ -21,8 +21,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
@@ -35,6 +33,9 @@
import android.content.Context;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.R;
import java.lang.annotation.Retention;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
index c9512cd..8eaea0e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelper.java
@@ -151,7 +151,9 @@
private boolean removePreferredDeviceForStrategies(List<AudioProductStrategy> strategies) {
boolean status = true;
for (AudioProductStrategy strategy : strategies) {
- status &= mAudioManager.removePreferredDeviceForStrategy(strategy);
+ if (mAudioManager.getPreferredDeviceForStrategy(strategy) != null) {
+ status &= mAudioManager.removePreferredDeviceForStrategy(strategy);
+ }
}
return status;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index 51164e8..57012aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -21,7 +21,6 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -31,6 +30,7 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 49ac0f8..de21c54 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -19,7 +19,6 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -44,6 +43,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index 34008ac..34c60a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -20,7 +20,6 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -33,6 +32,7 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
index 3774b88..ab7a3db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VolumeControlProfile.java
@@ -21,7 +21,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
-import android.annotation.NonNull;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -31,6 +30,7 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
index be420ac..43c6c0d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
@@ -16,7 +16,6 @@
package com.android.settingslib.connectivity;
-import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +30,7 @@
import android.telephony.TelephonyManager;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
index 067afa4..f847154 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
@@ -14,7 +14,6 @@
package com.android.settingslib.core.instrumentation;
-import android.annotation.Nullable;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
@@ -24,6 +23,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.util.Map;
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
index 2bd0b27..e974e70 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
@@ -22,13 +22,13 @@
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
-import android.annotation.Nullable;
import android.app.Activity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.Menu;
import android.view.MenuItem;
+import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index bbb1ec6..0029e20 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -16,7 +16,6 @@
package com.android.settingslib.datetime;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.icu.text.TimeZoneFormat;
@@ -29,6 +28,7 @@
import android.util.Log;
import android.view.View;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.core.text.TextDirectionHeuristicsCompat;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 11fae24..f07daa3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -22,7 +22,6 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
-import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -45,6 +44,7 @@
import android.os.Build;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
index 0b3a519..f329023 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
@@ -16,11 +16,11 @@
package com.android.settingslib.enterprise;
-import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.DialogInterface;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
index 714accc..7e3395b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -16,7 +16,6 @@
package com.android.settingslib.enterprise;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -24,6 +23,7 @@
import android.provider.Settings;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index 17c2b02..71d5809 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -16,7 +16,6 @@
package com.android.settingslib.graph;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -35,6 +34,8 @@
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.R;
import com.android.settingslib.Utils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
index f01eb2a..65d53f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
@@ -16,7 +16,7 @@
package com.android.settingslib.graph;
-import android.annotation.NonNull;
+
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PorterDuff;
@@ -25,6 +25,7 @@
import android.graphics.drawable.LayerDrawable;
import android.view.Gravity;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
index 4d0804e..9a19f93 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
@@ -16,8 +16,6 @@
import android.animation.ArgbEvaluator;
import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
@@ -36,6 +34,9 @@
import android.util.LayoutDirection;
import android.util.PathParser;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.R;
import com.android.settingslib.Utils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
index 1712a6b..318e3dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
@@ -16,8 +16,6 @@
package com.android.settingslib.inputmethod;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
@@ -30,6 +28,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragment;
import androidx.preference.PreferenceScreen;
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
index 78ec58b..a2316435 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
@@ -16,8 +16,6 @@
package com.android.settingslib.inputmethod;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
@@ -32,6 +30,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
index 4384400..5c0c979 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
@@ -17,7 +17,6 @@
package com.android.settingslib.inputmethod;
import android.annotation.AnyThread;
-import android.annotation.NonNull;
import android.annotation.UiThread;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,6 +25,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 52b51d7..e5fce5b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -43,8 +43,6 @@
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
@@ -59,6 +57,8 @@
import android.util.Log;
import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.internal.annotations.VisibleForTesting;
@@ -98,14 +98,15 @@
private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
new ConcurrentHashMap<>();
- public InfoMediaManager(Context context, String packageName, Notification notification,
+ public InfoMediaManager(
+ Context context,
+ @NonNull String packageName,
+ Notification notification,
LocalBluetoothManager localBluetoothManager) {
super(context, notification);
mBluetoothManager = localBluetoothManager;
- if (!TextUtils.isEmpty(packageName)) {
- mPackageName = packageName;
- }
+ mPackageName = packageName;
}
/** Creates an instance of InfoMediaManager. */
@@ -114,6 +115,14 @@
String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
+
+ // The caller is only interested in system routes (headsets, built-in speakers, etc), and is
+ // not interested in a specific app's routing. The media routing APIs still require a
+ // package name, so we use the package name of the calling app.
+ if (TextUtils.isEmpty(packageName)) {
+ packageName = context.getPackageName();
+ }
+
if (Flags.useMediaRouter2ForInfoMediaManager()) {
try {
return new RouterInfoMediaManager(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 0be2e0e..97bbf12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -16,8 +16,6 @@
package com.android.settingslib.media;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.media.MediaRoute2Info;
@@ -27,6 +25,9 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -80,14 +81,17 @@
@Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
- mRouterManager.transfer(mPackageName, route);
+ // TODO: b/279555229 - provide real user handle of a caller.
+ mRouterManager.transfer(mPackageName, route, android.os.Process.myUserHandle());
}
@Override
protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null);
if (info != null) {
- mRouterManager.transfer(info, device.mRouteInfo);
+ // TODO: b/279555229 - provide real user handle and package name of a caller.
+ mRouterManager.transfer(
+ info, device.mRouteInfo, android.os.Process.myUserHandle(), mPackageName);
return true;
}
return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 80eeab5..0676ce5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -29,7 +29,6 @@
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
import android.Manifest;
-import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
@@ -40,6 +39,7 @@
import android.media.RouteListingPreference;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index 5298a46..8a122fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -71,11 +71,6 @@
LocalBluetoothManager localBluetoothManager) throws PackageNotAvailableException {
super(context, packageName, notification, localBluetoothManager);
- // TODO: b/291277292 - Change optional package name for a mandatory uid.
- if (packageName == null) {
- packageName = context.getPackageName();
- }
-
mRouter = MediaRouter2.getInstance(context, packageName);
if (mRouter == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index 5e9ac5a..bcfdebe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -16,7 +16,6 @@
package com.android.settingslib.net;
-import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -27,6 +26,7 @@
import android.util.Pair;
import android.util.Range;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.loader.content.AsyncTaskLoader;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index 562d20d..dfa5ed1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -16,10 +16,10 @@
package com.android.settingslib.notification;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
+import android.app.Flags;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -42,6 +42,8 @@
import android.widget.ScrollView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.PhoneWindow;
import com.android.settingslib.R;
@@ -143,9 +145,16 @@
Slog.d(TAG, "Invalid manual condition: " + tag.condition);
}
// always triggers priority-only dnd with chosen condition
- mNotificationManager.setZenMode(
- Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- getRealConditionId(tag.condition), TAG);
+ if (Flags.modesApi()) {
+ mNotificationManager.setZenMode(
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ getRealConditionId(tag.condition), TAG,
+ /* fromUser= */ true);
+ } else {
+ mNotificationManager.setZenMode(
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ getRealConditionId(tag.condition), TAG);
+ }
}
});
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
index fbf8a2f..23b2cc2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
@@ -16,8 +16,6 @@
package com.android.settingslib.volume;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -42,6 +40,9 @@
import android.os.Message;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 303ee3c..cf45231 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -21,7 +21,6 @@
import android.annotation.IntDef;
import android.annotation.MainThread;
-import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -58,6 +57,7 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 98272cc..e508526 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -15,7 +15,6 @@
*/
package com.android.settingslib.wifi;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -32,6 +31,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
index 7ffae40..c0ca1252 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
@@ -18,12 +18,12 @@
import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
-import android.annotation.NonNull;
import android.content.Context;
import android.os.Bundle;
import android.os.UserManager;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.util.HashMap;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
index a0c2698..14967ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java
@@ -22,7 +22,6 @@
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import android.annotation.AnyThread;
-import android.annotation.NonNull;
import android.annotation.TestApi;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -34,6 +33,7 @@
import android.util.Log;
import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
index 8b5ea30..c835244 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidAudioRoutingHelperTest.java
@@ -18,8 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +45,7 @@
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -79,6 +82,8 @@
when(mAudioDeviceInfo.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(
new AudioDeviceInfo[]{mAudioDeviceInfo});
+ doReturn(Collections.emptyList()).when(mAudioManager).getPreferredDevicesForStrategy(
+ any(AudioProductStrategy.class));
when(mAudioStrategy.getAudioAttributesForLegacyStreamType(
AudioManager.STREAM_MUSIC))
.thenReturn((new AudioAttributes.Builder()).build());
@@ -92,7 +97,10 @@
}
@Test
- public void setPreferredDeviceRoutingStrategies_valueAuto_callRemoveStrategy() {
+ public void setPreferredDeviceRoutingStrategies_hadValueThenValueAuto_callRemoveStrategy() {
+ when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(
+ mHearingDeviceAttribute);
+
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
HearingAidAudioRoutingConstants.RoutingValue.AUTO);
@@ -101,6 +109,17 @@
}
@Test
+ public void setPreferredDeviceRoutingStrategies_NoValueThenValueAuto_notCallRemoveStrategy() {
+ when(mAudioManager.getPreferredDeviceForStrategy(mAudioStrategy)).thenReturn(null);
+
+ mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
+ mHearingDeviceAttribute,
+ HearingAidAudioRoutingConstants.RoutingValue.AUTO);
+
+ verify(mAudioManager, never()).removePreferredDeviceForStrategy(mAudioStrategy);
+ }
+
+ @Test
public void setPreferredDeviceRoutingStrategies_valueHearingDevice_callSetStrategy() {
mHelper.setPreferredDeviceRoutingStrategies(List.of(mAudioStrategy),
mHearingDeviceAttribute,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
index 5aee8cd..194a0e2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
@@ -27,9 +27,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.SwitchPreference;
import androidx.recyclerview.widget.RecyclerView;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
index 1d5f1b2..819d4b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
@@ -16,7 +16,7 @@
package com.android.settingslib.enterprise;
-import android.annotation.Nullable;
+import androidx.annotation.Nullable;
class FakeDeviceAdminStringProvider implements DeviceAdminStringProvider {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index c79440e..77c46f7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -26,7 +26,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -37,6 +36,8 @@
import android.text.format.DateUtils;
import android.util.Range;
+import androidx.annotation.NonNull;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
index fae3aea..8448804 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
@@ -16,12 +16,13 @@
package com.android.settingslib.testutils.shadow;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.AttributionSource;
import android.content.Context;
import android.content.PermissionChecker;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
index dac8142..fde378f 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -15,12 +15,13 @@
*/
package com.android.settingslib.testutils.shadow;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.RoutingSessionInfo;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index adebdcd..d5814e3 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -59,10 +59,10 @@
// Note we statically link SettingsProviderLib to do some unit tests. It's not accessible otherwise
// because this test is not an instrumentation test. (because the target runs in the system process.)
"SettingsProviderLib",
-
"androidx.test.rules",
"flag-junit",
"junit",
+ "libaconfig_java_proto_lite",
"mockito-target-minus-junit4",
"platform-test-annotations",
"truth",
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index e5dbe5f..d6e8d26 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -112,5 +112,7 @@
Settings.Global.Wearable.SCREENSHOT_ENABLED,
Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED,
Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
+ Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
+ Settings.Global.FORCE_ENABLE_PSS_PROFILING,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index d9fe733..f8bdcf6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -209,6 +209,7 @@
VALIDATORS.put(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Global.STYLUS_EVER_USED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
@@ -446,5 +447,7 @@
VALIDATORS.put(Global.Wearable.WEAR_LAUNCHER_UI_MODE, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_POWER_ANOMALY_SERVICE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.CONNECTIVITY_KEEP_DATA_ON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.FORCE_ENABLE_PSS_PROFILING, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 8f459c6..73c2e22 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -18,6 +18,9 @@
import static android.os.Process.FIRST_APPLICATION_UID;
+import android.aconfig.Aconfig.flag_state;
+import android.aconfig.Aconfig.parsed_flag;
+import android.aconfig.Aconfig.parsed_flags;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -147,6 +150,17 @@
*/
private static final String CONFIG_STAGED_PREFIX = "staged/";
+ private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
+ "/system/etc/aconfig_flags.pb",
+ "/system_ext/etc/aconfig_flags.pb",
+ "/product/etc/aconfig_flags.pb",
+ "/vendor/etc/aconfig_flags.pb");
+
+ /**
+ * This tag is applied to all aconfig default value-loaded flags.
+ */
+ private static final String BOOT_LOADED_DEFAULT_TAG = "BOOT_LOADED_DEFAULT";
+
// This was used in version 120 and before.
private static final String NULL_VALUE_OLD_STYLE = "null";
@@ -315,6 +329,59 @@
synchronized (mLock) {
readStateSyncLocked();
+
+ if (Flags.loadAconfigDefaults()) {
+ // Only load aconfig defaults if this is the first boot, the XML
+ // file doesn't exist yet, or this device is on its first boot after
+ // an OTA.
+ boolean shouldLoadAconfigValues = isConfigSettingsKey(mKey)
+ && (!file.exists()
+ || mContext.getPackageManager().isDeviceUpgrading());
+ if (shouldLoadAconfigValues) {
+ loadAconfigDefaultValuesLocked();
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void loadAconfigDefaultValuesLocked() {
+ for (String fileName : sAconfigTextProtoFilesOnDevice) {
+ try (FileInputStream inputStream = new FileInputStream(fileName)) {
+ byte[] contents = inputStream.readAllBytes();
+ loadAconfigDefaultValues(contents);
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "failed to read protobuf", e);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ public void loadAconfigDefaultValues(byte[] fileContents) {
+ try {
+ parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents);
+
+ if (parsedFlags == null) {
+ Slog.e(LOG_TAG, "failed to parse aconfig protobuf");
+ return;
+ }
+
+ for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
+ String flagName = flag.getNamespace() + "/"
+ + flag.getPackage() + "." + flag.getName();
+ String value = flag.getState() == flag_state.ENABLED ? "true" : "false";
+
+ Setting existingSetting = getSettingLocked(flagName);
+ boolean isDefaultLoaded = existingSetting.getTag() != null
+ && existingSetting.getTag().equals(BOOT_LOADED_DEFAULT_TAG);
+ if (existingSetting.getValue() == null || isDefaultLoaded) {
+ insertSettingLocked(flagName, value, BOOT_LOADED_DEFAULT_TAG,
+ false, flag.getPackage());
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "failed to parse protobuf", e);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index 27ce0d4..ecac5ee 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -6,3 +6,11 @@
description: "When enabled, allows setting and displaying local overrides via adb."
bug: "298392357"
}
+
+flag {
+ name: "load_aconfig_defaults"
+ namespace: "core_experiments_team_internal"
+ description: "When enabled, loads aconfig default values into DeviceConfig on boot."
+ bug: "311155098"
+ is_fixed_read_only: true
+}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 85e8769..6ad10cc 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -350,6 +350,7 @@
Settings.Global.DSRM_DURATION_MILLIS,
Settings.Global.DSRM_ENABLED_ACTIONS,
Settings.Global.MODE_RINGER,
+ Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE,
Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
Settings.Global.MULTI_SIM_SMS_PROMPT,
Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 02a7bc1..24625ea 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -15,6 +15,9 @@
*/
package com.android.providers.settings;
+import android.aconfig.Aconfig;
+import android.aconfig.Aconfig.parsed_flag;
+import android.aconfig.Aconfig.parsed_flags;
import android.os.Looper;
import android.test.AndroidTestCase;
import android.util.Xml;
@@ -84,6 +87,86 @@
super.tearDown();
}
+ public void testLoadValidAconfigProto() {
+ int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+ Object lock = new Object();
+ SettingsState settingsState = new SettingsState(
+ getContext(), lock, mSettingsFile, configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+ parsed_flags flags = parsed_flags
+ .newBuilder()
+ .addParsedFlag(parsed_flag
+ .newBuilder()
+ .setPackage("com.android.flags")
+ .setName("flag1")
+ .setNamespace("test_namespace")
+ .setDescription("test flag")
+ .addBug("12345678")
+ .setState(Aconfig.flag_state.DISABLED)
+ .setPermission(Aconfig.flag_permission.READ_WRITE))
+ .addParsedFlag(parsed_flag
+ .newBuilder()
+ .setPackage("com.android.flags")
+ .setName("flag2")
+ .setNamespace("test_namespace")
+ .setDescription("another test flag")
+ .addBug("12345678")
+ .setState(Aconfig.flag_state.ENABLED)
+ .setPermission(Aconfig.flag_permission.READ_WRITE))
+ .build();
+
+ synchronized (lock) {
+ settingsState.loadAconfigDefaultValues(flags.toByteArray());
+ settingsState.persistSettingsLocked();
+ }
+ settingsState.waitForHandler();
+
+ synchronized (lock) {
+ assertEquals("false",
+ settingsState.getSettingLocked(
+ "test_namespace/com.android.flags.flag1").getValue());
+ assertEquals("true",
+ settingsState.getSettingLocked(
+ "test_namespace/com.android.flags.flag2").getValue());
+ }
+ }
+
+ public void testSkipLoadingAconfigFlagWithMissingFields() {
+ int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+ Object lock = new Object();
+ SettingsState settingsState = new SettingsState(
+ getContext(), lock, mSettingsFile, configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+ parsed_flags flags = parsed_flags
+ .newBuilder()
+ .addParsedFlag(parsed_flag
+ .newBuilder()
+ .setDescription("test flag")
+ .addBug("12345678")
+ .setState(Aconfig.flag_state.DISABLED)
+ .setPermission(Aconfig.flag_permission.READ_WRITE))
+ .build();
+
+ synchronized (lock) {
+ settingsState.loadAconfigDefaultValues(flags.toByteArray());
+ settingsState.persistSettingsLocked();
+ }
+ settingsState.waitForHandler();
+
+ synchronized (lock) {
+ assertEquals(null,
+ settingsState.getSettingLocked(
+ "test_namespace/com.android.flags.flag1").getValue());
+ }
+ }
+
+ public void testInvalidAconfigProtoDoesNotCrash() {
+ SettingsState settingsState = getSettingStateObject();
+ settingsState.loadAconfigDefaultValues("invalid protobuf".getBytes());
+ }
+
public void testIsBinary() {
assertFalse(SettingsState.isBinary(" abc 日本語"));
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 6e65c16..477c42e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -688,6 +688,9 @@
<!-- Permission required for CTS test - CtsAmbientContextDetectionServiceDeviceTest -->
<uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
+ <!-- Permission required for CTS test - CtsWearableSensingServiceTestCases -->
+ <uses-permission android:name="android.permission.MANAGE_WEARABLE_SENSING_SERVICE" />
+
<!-- Permission required for CTS test - CallAudioInterceptionTest -->
<uses-permission android:name="android.permission.CALL_AUDIO_INTERCEPTION" />
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index 42b635a..1a4b009 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -21,7 +21,7 @@
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"गड़बड़ी की रिपोर्ट <xliff:g id="ID">#%d</xliff:g> तैयार की जा रही है"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"गड़बड़ी की रिपोर्ट <xliff:g id="ID">#%d</xliff:g> कैप्चर की गई"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"गड़बड़ी की रिपोर्ट में पूरी जानकारी जोड़ी जा रही है"</string>
- <string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा करें…"</string>
+ <string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया इंतज़ार करें…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"गड़बड़ी की रिपोर्ट थोड़ी ही देर में फ़ोन पर दिखाई देगी"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"अपनी गड़बड़ी की रिपोर्ट शेयर करने के लिए टैप करें"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"अपनी गड़बड़ी की रिपोर्ट शेयर करने के लिए टैप करें"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1a35f04..a03fa9b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -256,6 +256,9 @@
<!-- launcher apps -->
<uses-permission android:name="android.permission.ACCESS_SHORTCUTS" />
+ <!-- Permission to start Launcher's widget picker activity. -->
+ <uses-permission android:name="android.permission.START_WIDGET_PICKER_ACTIVITY" />
+
<uses-permission android:name="android.permission.MODIFY_THEME_OVERLAY" />
<!-- accessibility -->
@@ -974,15 +977,6 @@
android:excludeFromRecents="true"
android:visibleToInstantApps="true"/>
- <activity android:name="com.android.systemui.communal.widgets.WidgetPickerActivity"
- android:theme="@style/Theme.EditWidgetsActivity"
- android:excludeFromRecents="true"
- android:autoRemoveFromRecents="true"
- android:showOnLockScreen="true"
- android:launchMode="singleTop"
- android:exported="false">
- </activity>
-
<activity android:name="com.android.systemui.communal.widgets.EditWidgetsActivity"
android:theme="@style/Theme.EditWidgetsActivity"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 50ed7ab..7f16ca5 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -23,7 +23,8 @@
default_visibility: [
"//visibility:override",
"//frameworks/base/packages/SystemUI:__subpackages__",
- "//platform_testing:__subpackages__"
+ "//frameworks/libs/systemui/tracinglib:__subpackages__",
+ "//platform_testing:__subpackages__",
],
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ab4fe76..14bcac2 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -45,6 +45,13 @@
}
flag {
+ name: "notifications_improved_hun_animation"
+ namespace: "systemui"
+ description: "Adds a translateY animation, and other improvements to match the motion specs of the HUN Intro + Outro animations."
+ bug: "243302608"
+}
+
+flag {
name: "notification_lifetime_extension_refactor"
namespace: "systemui"
description: "Enables moving notification lifetime extension management from SystemUI to "
@@ -198,6 +205,20 @@
}
flag {
+ name: "pss_task_switcher"
+ namespace: "systemui"
+ description: "Enable the task switcher feature for partial screen sharing"
+ bug: "317208379"
+}
+
+flag {
+ name: "revamped_bouncer_messages"
+ namespace: "systemui"
+ description: "Change the bouncer message to be a 2-line more descriptive message"
+ bug: "236891644"
+}
+
+flag {
name: "rest_to_unlock"
namespace: "systemui"
description: "Require prolonged touch for fingerprint authentication"
@@ -241,6 +262,13 @@
}
flag {
+ name: "switch_user_on_bg"
+ namespace: "systemui"
+ description: "Does user switching on a background thread"
+ bug: "284095720"
+}
+
+flag {
name: "status_bar_static_inout_indicators"
namespace: "systemui"
description: "(Upstream request) Always show the network activity inout indicators and "
@@ -248,3 +276,30 @@
bug: "310715220"
}
+flag {
+ name: "haptic_volume_slider"
+ namespace: "systemui"
+ description: "Adds haptic feedback to the volume slider."
+ bug: "316953430"
+}
+
+flag {
+ name: "new_volume_panel"
+ namespace: "systemui"
+ description: "Switches to the new volume panel (without Slices)."
+ bug: "202262476"
+}
+
+flag {
+ name: "screenshare_notification_hiding"
+ namespace: "systemui"
+ description: "Enable hiding of notifications during screenshare"
+ bug: "312784809"
+}
+
+flag {
+ name: "bluetooth_qs_tile_dialog_auto_on_toggle"
+ namespace: "systemui"
+ description: "Displays the auto on toggle in the bluetooth QS tile dialog"
+ bug: "316985153"
+}
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 4d53cca..cbf2496 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
@@ -21,6 +21,7 @@
import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import com.android.systemui.keyguard.ui.composable.LockscreenScene
+import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModule
import com.android.systemui.scene.shared.model.Scene
import dagger.Binds
import dagger.Module
@@ -29,7 +30,12 @@
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
-@Module
+@Module(
+ includes =
+ [
+ LockscreenSceneBlueprintModule::class,
+ ],
+)
interface LockscreenSceneModule {
@Binds @IntoSet fun lockscreenScene(scene: LockscreenScene): Scene
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 1d7c7d63..6591543 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
@@ -19,7 +19,6 @@
package com.android.systemui.bouncer.ui.composable
import android.app.AlertDialog
-import android.app.Dialog
import android.content.DialogInterface
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState
@@ -135,7 +134,7 @@
}
Dialog(
- viewModel = viewModel,
+ bouncerViewModel = viewModel,
dialogFactory = dialogFactory,
)
}
@@ -666,32 +665,30 @@
@Composable
private fun Dialog(
- viewModel: BouncerViewModel,
+ bouncerViewModel: BouncerViewModel,
dialogFactory: BouncerDialogFactory,
) {
- val dialogMessage: String? by viewModel.dialogMessage.collectAsState()
- var dialog: Dialog? by remember { mutableStateOf(null) }
+ val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsState()
+ var dialog: AlertDialog? by remember { mutableStateOf(null) }
- if (dialogMessage != null) {
+ dialogViewModel?.let { viewModel ->
if (dialog == null) {
- dialog =
- dialogFactory().apply {
- setMessage(dialogMessage)
- setButton(
- DialogInterface.BUTTON_NEUTRAL,
- context.getString(R.string.ok),
- ) { _, _ ->
- viewModel.onDialogDismissed()
- }
- setCancelable(false)
- setCanceledOnTouchOutside(false)
- show()
- }
+ dialog = dialogFactory()
}
- } else {
- dialog?.dismiss()
- dialog = null
+ dialog?.apply {
+ setMessage(viewModel.text)
+ setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(R.string.ok)) { _, _ ->
+ viewModel.onDismiss()
+ }
+ setCancelable(false)
+ setCanceledOnTouchOutside(false)
+ show()
+ }
}
+ ?: {
+ dialog?.dismiss()
+ dialog = null
+ }
}
/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
@@ -772,7 +769,7 @@
}
/**
- * Renders the dropdowm menu that displays the actual users and/or user actions that can be
+ * Renders the dropdown menu that displays the actual users and/or user actions that can be
* selected.
*/
@Composable
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 2a9cf0f..55fc3a2 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
@@ -20,10 +20,12 @@
import android.util.SizeF
import android.widget.FrameLayout
import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -31,11 +33,13 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Edit
@@ -98,7 +102,6 @@
modifier = modifier.fillMaxSize().background(Color.White),
) {
CommunalHubLazyGrid(
- modifier = Modifier.align(Alignment.CenterStart),
communalContent = communalContent,
viewModel = viewModel,
contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize),
@@ -138,21 +141,21 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
-private fun CommunalHubLazyGrid(
+private fun BoxScope.CommunalHubLazyGrid(
communalContent: List<CommunalContentModel>,
viewModel: BaseCommunalViewModel,
- modifier: Modifier = Modifier,
contentPadding: PaddingValues,
setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
updateDragPositionForRemove: (offset: Offset) -> Boolean,
) {
- var gridModifier = modifier
+ var gridModifier = Modifier.align(Alignment.CenterStart)
val gridState = rememberLazyGridState()
var list = communalContent
var dragDropState: GridDragDropState? = null
if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
val contentListState = rememberContentListState(communalContent, viewModel)
list = contentListState.list
+ // for drag & drop operations within the communal hub grid
dragDropState =
rememberGridDragDropState(
gridState = gridState,
@@ -164,9 +167,22 @@
.fillMaxSize()
.dragContainer(dragDropState, beforeContentPadding(contentPadding))
.onGloballyPositioned { setGridCoordinates(it) }
+ // for widgets dropped from other activities
+ val dragAndDropTargetState =
+ rememberDragAndDropTargetState(
+ gridState = gridState,
+ contentListState = contentListState,
+ updateDragPositionForRemove = updateDragPositionForRemove
+ )
+
+ // A full size box in background that listens to widget drops from the picker.
+ // Since the grid has its own listener for in-grid drag events, we use a separate element
+ // for android drag events.
+ Box(Modifier.fillMaxSize().dragAndDropTarget(dragAndDropTargetState)) {}
} else {
gridModifier = gridModifier.height(Dimensions.GridHeight)
}
+
LazyHorizontalGrid(
modifier = gridModifier,
state = gridState,
@@ -309,12 +325,24 @@
) {
when (model) {
is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier)
+ is CommunalContentModel.WidgetPlaceholder -> WidgetPlaceholderContent(size)
is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
is CommunalContentModel.Umo -> Umo(viewModel, modifier)
}
}
+/** Presents a placeholder card for the new widget being dragged and dropping into the grid. */
+@Composable
+fun WidgetPlaceholderContent(size: SizeF) {
+ Card(
+ modifier = Modifier.size(Dp(size.width), Dp(size.height)),
+ colors = CardDefaults.cardColors(containerColor = Color.Transparent),
+ border = BorderStroke(3.dp, LocalAndroidColorScheme.current.tertiaryFixed),
+ shape = RoundedCornerShape(16.dp)
+ ) {}
+}
+
@Composable
private fun WidgetContent(
model: CommunalContentModel.Widget,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 89c5765..979991d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -16,11 +16,10 @@
package com.android.systemui.communal.ui.compose
+import android.content.ComponentName
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.toMutableStateList
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
@@ -32,6 +31,7 @@
return remember(communalContent) {
ContentListState(
communalContent,
+ viewModel::onAddWidget,
viewModel::onDeleteWidget,
viewModel::onReorderWidgets,
)
@@ -46,30 +46,57 @@
class ContentListState
internal constructor(
communalContent: List<CommunalContentModel>,
+ private val onAddWidget: (componentName: ComponentName, priority: Int) -> Unit,
private val onDeleteWidget: (id: Int) -> Unit,
- private val onReorderWidgets: (ids: List<Int>) -> Unit,
+ private val onReorderWidgets: (widgetIdToPriorityMap: Map<Int, Int>) -> Unit,
) {
- var list by mutableStateOf(communalContent)
+ var list = communalContent.toMutableStateList()
private set
/** Move item to a new position in the list. */
fun onMove(fromIndex: Int, toIndex: Int) {
- list = list.toMutableList().apply { add(toIndex, removeAt(fromIndex)) }
+ list.apply { add(toIndex, removeAt(fromIndex)) }
}
/** Remove widget from the list and the database. */
fun onRemove(indexToRemove: Int) {
if (list[indexToRemove] is CommunalContentModel.Widget) {
val widget = list[indexToRemove] as CommunalContentModel.Widget
- list = list.toMutableList().apply { removeAt(indexToRemove) }
+ list.apply { removeAt(indexToRemove) }
onDeleteWidget(widget.appWidgetId)
}
}
- /** Persist the new order with all the movements happened during dragging. */
- fun onSaveList() {
- val widgetIds: List<Int> =
- list.filterIsInstance<CommunalContentModel.Widget>().map { it.appWidgetId }
- onReorderWidgets(widgetIds)
+ /**
+ * Persists the new order with all the movements happened during drag operations & the new
+ * widget drop (if applicable).
+ *
+ * @param newItemComponentName name of the new widget that was dropped into the list; null if no
+ * new widget was added.
+ * @param newItemIndex index at which the a new widget was dropped into the list; null if no new
+ * widget was dropped.
+ */
+ fun onSaveList(newItemComponentName: ComponentName? = null, newItemIndex: Int? = null) {
+ // filters placeholder, but, maintains the indices of the widgets as if the placeholder was
+ // in the list. When persisted in DB, this leaves space for the new item (to be added) at
+ // the correct priority.
+ val widgetIdToPriorityMap: Map<Int, Int> =
+ list
+ .mapIndexedNotNull { index, item ->
+ if (item is CommunalContentModel.Widget) {
+ item.appWidgetId to list.size - index
+ } else {
+ null
+ }
+ }
+ .toMap()
+ // reorder and then add the new widget
+ onReorderWidgets(widgetIdToPriorityMap)
+ if (newItemComponentName != null && newItemIndex != null) {
+ onAddWidget(newItemComponentName, /*priority=*/ list.size - newItemIndex)
+ }
}
+
+ /** Returns true if the item at given index is editable. */
+ fun isItemEditable(index: Int) = list[index] is CommunalContentModel.Widget
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
new file mode 100644
index 0000000..22aa837
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.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.systemui.communal.ui.compose
+
+import android.content.ClipDescription
+import android.content.ComponentName
+import android.content.Intent
+import android.view.DragEvent
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.draganddrop.dragAndDropTarget
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
+import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draganddrop.DragAndDropEvent
+import androidx.compose.ui.draganddrop.DragAndDropTarget
+import androidx.compose.ui.draganddrop.mimeTypes
+import androidx.compose.ui.draganddrop.toAndroidDragEvent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.ui.compose.extensions.plus
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+
+/**
+ * Holds state associated with dragging and dropping items from other activities into the lazy grid.
+ *
+ * @see dragAndDropTarget
+ */
+@Composable
+internal fun rememberDragAndDropTargetState(
+ gridState: LazyGridState,
+ contentListState: ContentListState,
+ updateDragPositionForRemove: (offset: Offset) -> Boolean,
+): DragAndDropTargetState {
+ val scope = rememberCoroutineScope()
+ val autoScrollSpeed = remember { mutableFloatStateOf(0f) }
+ // Threshold of distance from edges that should start auto-scroll - chosen to be a narrow value
+ // that allows differentiating intention of scrolling from intention of dragging over the first
+ // visible item.
+ val autoScrollThreshold = with(LocalDensity.current) { 60.dp.toPx() }
+ val state =
+ remember(gridState, contentListState) {
+ DragAndDropTargetState(
+ state = gridState,
+ contentListState = contentListState,
+ scope = scope,
+ autoScrollSpeed = autoScrollSpeed,
+ autoScrollThreshold = autoScrollThreshold,
+ updateDragPositionForRemove = updateDragPositionForRemove,
+ )
+ }
+ LaunchedEffect(autoScrollSpeed.floatValue) {
+ if (autoScrollSpeed.floatValue != 0f) {
+ while (isActive) {
+ gridState.scrollBy(autoScrollSpeed.floatValue)
+ delay(10)
+ }
+ }
+ }
+ return state
+}
+
+/**
+ * Attaches a listener for drag and drop events from other activities.
+ *
+ * @see androidx.compose.foundation.draganddrop.dragAndDropTarget
+ * @see DragEvent
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+internal fun Modifier.dragAndDropTarget(
+ dragDropTargetState: DragAndDropTargetState,
+): Modifier {
+ val state by rememberUpdatedState(dragDropTargetState)
+
+ return this then
+ Modifier.dragAndDropTarget(
+ shouldStartDragAndDrop = accept@{ startEvent ->
+ startEvent.mimeTypes().any { it == ClipDescription.MIMETYPE_TEXT_INTENT }
+ },
+ target =
+ object : DragAndDropTarget {
+ override fun onStarted(event: DragAndDropEvent) {
+ state.onStarted()
+ }
+
+ override fun onMoved(event: DragAndDropEvent) {
+ state.onMoved(event)
+ }
+
+ override fun onDrop(event: DragAndDropEvent): Boolean {
+ return state.onDrop(event)
+ }
+
+ override fun onEnded(event: DragAndDropEvent) {
+ state.onEnded()
+ }
+ }
+ )
+}
+
+/**
+ * Handles dropping of an item coming from a different activity (e.g. widget picker) in to the grid
+ * corresponding to the provided [LazyGridState].
+ *
+ * Adds a placeholder container to highlight the anticipated location the widget will be dropped to.
+ * When the item is held over an empty area, the placeholder appears at the end of the grid if one
+ * didn't exist already. As user moves the item over an existing item, the placeholder appears in
+ * place of that existing item. And then, the existing item is pushed over as part of re-ordering.
+ *
+ * Once item is dropped, new ordering along with the dropped item is persisted. See
+ * [ContentListState.onSaveList].
+ *
+ * Difference between this and [GridDragDropState] is that, this is used for listening to drops from
+ * other activities. [GridDragDropState] on the other hand, handles dragging of existing items in
+ * the communal hub grid.
+ */
+internal class DragAndDropTargetState(
+ private val state: LazyGridState,
+ private val contentListState: ContentListState,
+ private val scope: CoroutineScope,
+ private val autoScrollSpeed: MutableState<Float>,
+ private val autoScrollThreshold: Float,
+ private val updateDragPositionForRemove: (offset: Offset) -> Boolean,
+) {
+ /**
+ * The placeholder item that is treated as if it is being dragged across the grid. It is added
+ * to grid once drag and drop event is started and removed when event ends.
+ */
+ private var placeHolder = CommunalContentModel.WidgetPlaceholder()
+
+ private var placeHolderIndex: Int? = null
+ private var isOnRemoveButton = false
+
+ fun onStarted() {
+ // assume item will be added to the end.
+ contentListState.list.add(placeHolder)
+ placeHolderIndex = contentListState.list.size - 1
+ }
+
+ fun onMoved(event: DragAndDropEvent) {
+ val dragEvent = event.toAndroidDragEvent()
+ isOnRemoveButton = updateDragPositionForRemove(Offset(dragEvent.x, dragEvent.y))
+ if (!isOnRemoveButton) {
+ findTargetItem(dragEvent)?.apply {
+ var scrollIndex: Int? = null
+ var scrollOffset: Int? = null
+ if (placeHolderIndex == state.firstVisibleItemIndex) {
+ // Save info about the first item before the move, to neutralize the automatic
+ // keeping first item first.
+ scrollIndex = placeHolderIndex
+ scrollOffset = state.firstVisibleItemScrollOffset
+ }
+
+ autoScrollIfNearEdges(dragEvent)
+
+ if (contentListState.isItemEditable(this.index)) {
+ movePlaceholderTo(this.index)
+ placeHolderIndex = this.index
+ }
+
+ if (scrollIndex != null && scrollOffset != null) {
+ // this is needed to neutralize automatic keeping the first item first.
+ scope.launch { state.scrollToItem(scrollIndex, scrollOffset) }
+ }
+ }
+ }
+ }
+
+ fun onDrop(event: DragAndDropEvent): Boolean {
+ autoScrollSpeed.value = 0f
+ if (isOnRemoveButton) {
+ return false
+ }
+ return placeHolderIndex?.let { dropIndex ->
+ val componentName = event.maybeWidgetComponentName()
+ if (componentName != null) {
+ // Placeholder isn't removed yet to allow the setting the right priority for items
+ // before adding in the new item.
+ contentListState.onSaveList(
+ newItemComponentName = componentName,
+ newItemIndex = dropIndex
+ )
+ return@let true
+ }
+ return false
+ }
+ ?: false
+ }
+
+ fun onEnded() {
+ autoScrollSpeed.value = 0f
+ placeHolderIndex = null
+ contentListState.list.remove(placeHolder)
+ isOnRemoveButton = updateDragPositionForRemove(Offset.Zero)
+ }
+
+ private fun autoScrollIfNearEdges(dragEvent: DragEvent) {
+ val orientation = state.layoutInfo.orientation
+ val distanceFromStart =
+ if (orientation == Orientation.Horizontal) {
+ dragEvent.x
+ } else {
+ dragEvent.y
+ }
+ val distanceFromEnd =
+ if (orientation == Orientation.Horizontal) {
+ state.layoutInfo.viewportSize.width - dragEvent.x
+ } else {
+ state.layoutInfo.viewportSize.height - dragEvent.y
+ }
+ autoScrollSpeed.value =
+ when {
+ distanceFromEnd < autoScrollThreshold -> autoScrollThreshold - distanceFromEnd
+ distanceFromStart < autoScrollThreshold ->
+ -(autoScrollThreshold - distanceFromStart)
+ else -> 0f
+ }
+ }
+
+ private fun findTargetItem(dragEvent: DragEvent): LazyGridItemInfo? =
+ state.layoutInfo.visibleItemsInfo.firstOrNull { item ->
+ dragEvent.x.toInt() in item.offset.x..(item.offset + item.size).x &&
+ dragEvent.y.toInt() in item.offset.y..(item.offset + item.size).y
+ }
+
+ private fun movePlaceholderTo(index: Int) {
+ val currentIndex = contentListState.list.indexOf(placeHolder)
+ if (currentIndex != index) {
+ contentListState.onMove(currentIndex, index)
+ }
+ }
+
+ /**
+ * Parses and returns the component name of the widget that was dropped into the communal grid.
+ *
+ * Returns null if the drop event didn't include the widget information.
+ */
+ private fun DragAndDropEvent.maybeWidgetComponentName(): ComponentName? {
+ val clipData = this.toAndroidDragEvent().clipData.takeIf { it.itemCount != 0 }
+ return clipData
+ ?.getItemAt(0)
+ ?.intent
+ ?.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 5451d05..0d460aa8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -32,15 +32,13 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.zIndex
-import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.ui.compose.extensions.plus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
@@ -112,7 +110,7 @@
.firstOrNull { item ->
// grid item offset is based off grid content container so we need to deduct
// before content padding from the initial pointer position
- item.isEditable &&
+ contentListState.isItemEditable(item.index) &&
(offset.x - contentOffset.x).toInt() in item.offset.x..item.offsetEnd.x &&
(offset.y - contentOffset.y).toInt() in item.offset.y..item.offsetEnd.y
}
@@ -149,7 +147,7 @@
val targetItem =
state.layoutInfo.visibleItemsInfo.find { item ->
- item.isEditable &&
+ contentListState.isItemEditable(item.index) &&
middleOffset.x.toInt() in item.offset.x..item.offsetEnd.x &&
middleOffset.y.toInt() in item.offset.y..item.offsetEnd.y &&
draggingItem.index != item.index
@@ -187,10 +185,6 @@
private val LazyGridItemInfo.offsetEnd: IntOffset
get() = this.offset + this.size
- /** Whether the grid item can be dragged or be a drop target. Only widget card is editable. */
- private val LazyGridItemInfo.isEditable: Boolean
- get() = contentListState.list[this.index] is CommunalContentModel.Widget
-
/** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */
private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float {
return when {
@@ -210,14 +204,6 @@
}
}
-private operator fun IntOffset.plus(size: IntSize): IntOffset {
- return IntOffset(x + size.width, y + size.height)
-}
-
-private operator fun Offset.plus(size: Size): Offset {
- return Offset(x + size.width, y + size.height)
-}
-
fun Modifier.dragContainer(
dragDropState: GridDragDropState,
beforeContentPadding: ContentPaddingInPx
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/OffsetOperators.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/OffsetOperators.kt
new file mode 100644
index 0000000..b86c07e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/extensions/OffsetOperators.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.communal.ui.compose.extensions
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+
+/** Adds the given size to the x and y offsets in this [IntOffset] */
+operator fun IntOffset.plus(size: IntSize): IntOffset {
+ return IntOffset(x + size.width, y + size.height)
+}
+
+/** Adds the given size to the x and y offsets in this [Offset]. */
+operator fun Offset.plus(size: Size): Offset {
+ return Offset(x + size.width, y + size.height)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
new file mode 100644
index 0000000..2cb0034
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.keyguard.ui.composable
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.transitions
+import com.android.systemui.keyguard.ui.composable.blueprint.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import javax.inject.Inject
+
+/**
+ * Renders the content of the lockscreen.
+ *
+ * This is separate from the [LockscreenScene] because it's meant to support usage of this UI from
+ * outside the scene container framework.
+ */
+class LockscreenContent
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+ private val blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+) {
+
+ private val sceneKeyByBlueprint: Map<LockscreenSceneBlueprint, SceneKey> by lazy {
+ blueprints.associateWith { blueprint -> SceneKey(blueprint.id) }
+ }
+ private val sceneKeyByBlueprintId: Map<String, SceneKey> by lazy {
+ sceneKeyByBlueprint.entries.associate { (blueprint, sceneKey) -> blueprint.id to sceneKey }
+ }
+
+ @Composable
+ fun Content(
+ modifier: Modifier = Modifier,
+ ) {
+ val coroutineScope = rememberCoroutineScope()
+ val blueprintId by viewModel.blueprintId(coroutineScope).collectAsState()
+
+ // Switch smoothly between blueprints, any composable tagged with element() will be
+ // transition-animated between any two blueprints, if they both display the same element.
+ SceneTransitionLayout(
+ currentScene = checkNotNull(sceneKeyByBlueprintId[blueprintId]),
+ onChangeScene = {},
+ transitions =
+ transitions { sceneKeyByBlueprintId.values.forEach { sceneKey -> to(sceneKey) } },
+ modifier = modifier,
+ ) {
+ sceneKeyByBlueprint.entries.forEach { (blueprint, sceneKey) ->
+ scene(sceneKey) { with(blueprint) { Content(Modifier.fillMaxSize()) } }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
new file mode 100644
index 0000000..472484a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -0,0 +1,71 @@
+/*
+ * 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(ExperimentalFoundationApi::class)
+
+package com.android.systemui.keyguard.ui.composable
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.input.pointer.pointerInput
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+
+/** Container for lockscreen content that handles long-press to bring up the settings menu. */
+@Composable
+fun LockscreenLongPress(
+ viewModel: KeyguardLongPressViewModel,
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
+) {
+ val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+ val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
+ val interactionSource = remember { MutableInteractionSource() }
+
+ Box(
+ modifier =
+ modifier
+ .combinedClickable(
+ enabled = isEnabled,
+ onLongClick = viewModel::onLongPress,
+ onClick = {},
+ interactionSource = interactionSource,
+ // Passing null for the indication removes the ripple effect.
+ indication = null,
+ )
+ .pointerInput(settingsMenuBounds) {
+ awaitEachGesture {
+ val pointerInputChange = awaitFirstDown()
+ if (settingsMenuBounds?.contains(pointerInputChange.position) == false) {
+ viewModel.onTouchedOutside()
+ }
+ }
+ },
+ ) {
+ content(setSettingsMenuBounds)
+ }
+}
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 3053654..67a6820 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
@@ -14,50 +14,32 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
-
package com.android.systemui.keyguard.ui.composable
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.gestures.awaitEachGesture
-import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.graphics.toComposeRect
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.qualifiers.KeyguardRootView
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
-import com.android.systemui.notifications.ui.composable.NotificationStack
-import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.ui.composable.ComposableScene
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+/** Set this to `true` to use the LockscreenContent replacement of KeyguardRootView. */
+private val UseLockscreenContent = false
+
/** The lock screen scene shows when the device is locked. */
@SysUISingleton
class LockscreenScene
@@ -65,7 +47,8 @@
constructor(
@Application private val applicationScope: CoroutineScope,
private val viewModel: LockscreenSceneViewModel,
- @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
+ private val lockscreenContent: Lazy<LockscreenContent>,
+ private val viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
) : ComposableScene {
override val key = SceneKey.Lockscreen
@@ -89,8 +72,8 @@
modifier: Modifier,
) {
LockscreenScene(
- viewProvider = viewProvider,
- viewModel = viewModel,
+ lockscreenContent = lockscreenContent,
+ viewBasedLockscreenContent = viewBasedLockscreenContent,
modifier = modifier,
)
}
@@ -111,89 +94,21 @@
@Composable
private fun SceneScope.LockscreenScene(
- viewProvider: () -> View,
- viewModel: LockscreenSceneViewModel,
+ lockscreenContent: Lazy<LockscreenContent>,
+ viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
modifier: Modifier = Modifier,
) {
- fun findSettingsMenu(): View {
- return viewProvider().requireViewById(R.id.keyguard_settings_button)
- }
-
- Box(
- modifier = modifier,
- ) {
- LongPressSurface(
- viewModel = viewModel.longPress,
- isSettingsMenuVisible = { findSettingsMenu().isVisible },
- settingsMenuBounds = {
- val bounds = android.graphics.Rect()
- findSettingsMenu().getHitRect(bounds)
- bounds.toComposeRect()
- },
- modifier = Modifier.fillMaxSize(),
- )
-
- AndroidView(
- factory = { _ ->
- val keyguardRootView = viewProvider()
- // Remove the KeyguardRootView from any parent it might already have in legacy code
- // just in case (a view can't have two parents).
- (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
- keyguardRootView
- },
- modifier = Modifier.fillMaxSize(),
- )
-
- val notificationStackPosition by viewModel.keyguardRoot.notificationBounds.collectAsState()
-
- Layout(
- modifier = Modifier.fillMaxSize(),
- content = {
- NotificationStack(
- viewModel = viewModel.notifications,
- isScrimVisible = false,
- )
- }
- ) { measurables, constraints ->
- check(measurables.size == 1)
- val height = notificationStackPosition.height.toInt()
- val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
- val placeable = measurables[0].measure(childConstraints)
- layout(constraints.maxWidth, constraints.maxHeight) {
- val start = (constraints.maxWidth - placeable.measuredWidth) / 2
- placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
- }
+ if (UseLockscreenContent) {
+ lockscreenContent
+ .get()
+ .Content(
+ modifier = modifier.fillMaxSize(),
+ )
+ } else {
+ with(viewBasedLockscreenContent.get()) {
+ Content(
+ modifier = modifier.fillMaxSize(),
+ )
}
}
}
-
-@Composable
-private fun LongPressSurface(
- viewModel: KeyguardLongPressViewModel,
- isSettingsMenuVisible: () -> Boolean,
- settingsMenuBounds: () -> Rect,
- modifier: Modifier = Modifier,
-) {
- val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
-
- Box(
- modifier =
- modifier
- .combinedClickable(
- enabled = isEnabled,
- onLongClick = viewModel::onLongPress,
- onClick = {},
- )
- .pointerInput(Unit) {
- awaitEachGesture {
- val pointerInputChange = awaitFirstDown()
- if (
- isSettingsMenuVisible() &&
- !settingsMenuBounds().contains(pointerInputChange.position)
- ) {
- viewModel.onTouchedOutside()
- }
- }
- },
- )
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
new file mode 100644
index 0000000..9abb50c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.keyguard.ui.composable
+
+import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintModule
+import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
+import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
+import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeBlueprintModule
+import dagger.Module
+
+@Module(
+ includes =
+ [
+ CommunalBlueprintModule::class,
+ DefaultBlueprintModule::class,
+ ShortcutsBesideUdfpsBlueprintModule::class,
+ SplitShadeBlueprintModule::class,
+ ],
+)
+interface LockscreenSceneBlueprintModule
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
new file mode 100644
index 0000000..976161b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.keyguard.ui.composable
+
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.toComposeRect
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * Renders the content of the lockscreen.
+ *
+ * This is different from [LockscreenContent] (which is pure compose) and uses a view-based
+ * implementation of the lockscreen scene content that relies on [KeyguardRootView].
+ *
+ * TODO(b/316211368): remove this once [LockscreenContent] is feature complete.
+ */
+class ViewBasedLockscreenContent
+@Inject
+constructor(
+ private val viewModel: LockscreenSceneViewModel,
+ @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
+) {
+ @Composable
+ fun SceneScope.Content(
+ modifier: Modifier = Modifier,
+ ) {
+ fun findSettingsMenu(): View {
+ return viewProvider().requireViewById(R.id.keyguard_settings_button)
+ }
+
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { onSettingsMenuPlaced ->
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy
+ // code just in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ keyguardRootView
+ },
+ modifier = Modifier.fillMaxSize(),
+ )
+
+ val notificationStackPosition by
+ viewModel.keyguardRoot.notificationBounds.collectAsState()
+
+ Layout(
+ modifier =
+ Modifier.fillMaxSize().onPlaced {
+ val settingsMenuView = findSettingsMenu()
+ onSettingsMenuPlaced(
+ if (settingsMenuView.isVisible) {
+ val bounds = Rect()
+ settingsMenuView.getHitRect(bounds)
+ bounds.toComposeRect()
+ } else {
+ null
+ }
+ )
+ },
+ content = {
+ NotificationStack(
+ viewModel = viewModel.notifications,
+ isScrimVisible = false,
+ )
+ }
+ ) { measurables, constraints ->
+ check(measurables.size == 1)
+ val height = notificationStackPosition.height.toInt()
+ val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
+ val placeable = measurables[0].measure(childConstraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ val start = (constraints.maxWidth - placeable.measuredWidth) / 2
+ placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
new file mode 100644
index 0000000..efa8cc7
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BlueprintAlignmentLines.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.keyguard.ui.composable.blueprint
+
+import androidx.compose.ui.layout.HorizontalAlignmentLine
+import androidx.compose.ui.layout.VerticalAlignmentLine
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Encapsulates all blueprint alignment lines.
+ *
+ * These can be used to communicate alignment lines emitted by elements that the blueprint should
+ * consume and use to know how to constrain and/or place other elements in that blueprint.
+ *
+ * For more information, please see
+ * [the official documentation](https://developer.android.com/jetpack/compose/layouts/alignment-lines).
+ */
+object BlueprintAlignmentLines {
+
+ /**
+ * Encapsulates alignment lines produced by the lock icon element.
+ *
+ * Because the lock icon is also the same element as the under-display fingerprint sensor
+ * (UDFPS), blueprints should use its alignment lines to make sure that other elements on screen
+ * do not overlap with the lock icon.
+ */
+ object LockIcon {
+
+ /** The left edge of the lock icon. */
+ val Left =
+ VerticalAlignmentLine(
+ merger = { old, new ->
+ // When two left alignment line values are provided, choose the leftmost one:
+ min(old, new)
+ },
+ )
+
+ /** The top edge of the lock icon. */
+ val Top =
+ HorizontalAlignmentLine(
+ merger = { old, new ->
+ // When two top alignment line values are provided, choose the topmost one:
+ min(old, new)
+ },
+ )
+
+ /** The right edge of the lock icon. */
+ val Right =
+ VerticalAlignmentLine(
+ merger = { old, new ->
+ // When two right alignment line values are provided, choose the rightmost one:
+ max(old, new)
+ },
+ )
+
+ /** The bottom edge of the lock icon. */
+ val Bottom =
+ HorizontalAlignmentLine(
+ merger = { old, new ->
+ // When two bottom alignment line values are provided, choose the bottommost
+ // one:
+ max(old, new)
+ },
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
new file mode 100644
index 0000000..86124c6
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.keyguard.ui.composable.blueprint
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+
+/** Renders the lockscreen scene when showing the communal glanceable hub. */
+class CommunalBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
+
+ override val id: String = "communal"
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { _ ->
+ Box(modifier.background(Color.Black)) {
+ Text(
+ text = "TODO(b/316211368): communal blueprint",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+ }
+}
+
+@Module
+interface CommunalBlueprintModule {
+ @Binds @IntoSet fun blueprint(blueprint: CommunalBlueprint): LockscreenSceneBlueprint
+}
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
new file mode 100644
index 0000000..d9d98cb
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -0,0 +1,184 @@
+/*
+ * 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.keyguard.ui.composable.blueprint
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.IntRect
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
+import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
+import com.android.systemui.keyguard.ui.composable.section.ClockSection
+import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
+import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
+import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+
+/**
+ * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
+ * factor).
+ */
+class DefaultBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+ private val statusBarSection: StatusBarSection,
+ private val clockSection: ClockSection,
+ private val smartSpaceSection: SmartSpaceSection,
+ private val notificationSection: NotificationSection,
+ private val lockSection: LockSection,
+ private val ambientIndicationSection: AmbientIndicationSection,
+ private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
+) : LockscreenSceneBlueprint {
+
+ override val id: String = "default"
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ val isUdfpsVisible = viewModel.isUdfpsVisible
+
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ with(lockSection) { LockIcon() }
+
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(bottomAreaSection) {
+ Shortcut(isStart = true, applyPadding = true)
+ Shortcut(isStart = false, applyPadding = true)
+ }
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val lockIconMeasurable = measurables[1]
+ val belowLockIconMeasurable = measurables[2]
+ val startShortcutMeasurable = measurables[3]
+ val endShortcutMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight = constraints.maxHeight - lockIconBounds.bottom
+ )
+ )
+ val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+ val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ startShortcutPleaceable.place(
+ x = 0,
+ y = constraints.maxHeight - startShortcutPleaceable.height,
+ )
+ endShortcutPleaceable.place(
+ x = constraints.maxWidth - endShortcutPleaceable.width,
+ y = constraints.maxHeight - endShortcutPleaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
+ }
+ }
+ }
+}
+
+@Module
+interface DefaultBlueprintModule {
+ @Binds @IntoSet fun blueprint(blueprint: DefaultBlueprint): LockscreenSceneBlueprint
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
new file mode 100644
index 0000000..6d9cba4
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.keyguard.ui.composable.blueprint
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneScope
+
+/** Defines interface for classes that can render the content for a specific blueprint/layout. */
+interface LockscreenSceneBlueprint {
+
+ /** The ID that uniquely identifies this blueprint across all other blueprints. */
+ val id: String
+
+ /** Renders the content of this blueprint. */
+ @Composable fun SceneScope.Content(modifier: Modifier)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
new file mode 100644
index 0000000..4704f5c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.keyguard.ui.composable.blueprint
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.unit.IntRect
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
+import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
+import com.android.systemui.keyguard.ui.composable.section.ClockSection
+import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
+import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
+import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+
+/**
+ * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
+ * factor).
+ */
+class ShortcutsBesideUdfpsBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+ private val statusBarSection: StatusBarSection,
+ private val clockSection: ClockSection,
+ private val smartSpaceSection: SmartSpaceSection,
+ private val notificationSection: NotificationSection,
+ private val lockSection: LockSection,
+ private val ambientIndicationSection: AmbientIndicationSection,
+ private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
+) : LockscreenSceneBlueprint {
+
+ override val id: String = "shortcuts-besides-udfps"
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ val isUdfpsVisible = viewModel.isUdfpsVisible
+
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { SmallClock(modifier = Modifier.fillMaxWidth()) }
+ with(smartSpaceSection) { SmartSpace(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(notificationSection) {
+ Notifications(modifier = Modifier.fillMaxWidth().weight(1f))
+ }
+ if (!isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ // Constrained to the left of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
+
+ with(lockSection) { LockIcon() }
+
+ // Constrained to the right of the lock icon (in left-to-right layouts).
+ with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
+
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible) {
+ with(ambientIndicationSection) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val startSideShortcutMeasurable = measurables[1]
+ val lockIconMeasurable = measurables[2]
+ val endSideShortcutMeasurable = measurables[3]
+ val belowLockIconMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val startSideShortcutPlaceable =
+ startSideShortcutMeasurable.measure(noMinConstraints)
+ val endSideShortcutPlaceable = endSideShortcutMeasurable.measure(noMinConstraints)
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight = constraints.maxHeight - lockIconBounds.bottom
+ )
+ )
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ startSideShortcutPlaceable.placeRelative(
+ x = lockIconBounds.left / 2 - startSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - startSideShortcutPlaceable.height / 2,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ endSideShortcutPlaceable.placeRelative(
+ x =
+ lockIconBounds.right +
+ (constraints.maxWidth - lockIconBounds.right) / 2 -
+ endSideShortcutPlaceable.width / 2,
+ y = lockIconBounds.center.y - endSideShortcutPlaceable.height / 2,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
+ }
+ }
+ }
+}
+
+@Module
+interface ShortcutsBesideUdfpsBlueprintModule {
+ @Binds
+ @IntoSet
+ fun blueprint(blueprint: ShortcutsBesideUdfpsBlueprint): LockscreenSceneBlueprint
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
new file mode 100644
index 0000000..fdf1166
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.keyguard.ui.composable.blueprint
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+
+/**
+ * Renders the lockscreen scene when showing with a split shade (e.g. unfolded foldable and/or
+ * tablet form factor).
+ */
+class SplitShadeBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+) : LockscreenSceneBlueprint {
+
+ override val id: String = "split-shade"
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { _ ->
+ Box(modifier.background(Color.Black)) {
+ Text(
+ text = "TODO(b/316211368): split shade blueprint",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+ }
+}
+
+@Module
+interface SplitShadeBlueprintModule {
+ @Binds @IntoSet fun blueprint(blueprint: SplitShadeBlueprint): LockscreenSceneBlueprint
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
new file mode 100644
index 0000000..0e7ac5e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/AmbientIndicationSection.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import javax.inject.Inject
+
+class AmbientIndicationSection @Inject constructor() {
+ @Composable
+ fun SceneScope.AmbientIndication(modifier: Modifier = Modifier) {
+ MovableElement(
+ key = AmbientIndicationElementKey,
+ modifier = modifier,
+ ) {
+ Box(
+ modifier = Modifier.fillMaxWidth().background(Color.Green),
+ ) {
+ Text(
+ text = "TODO(b/316211368): Ambient indication",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+ }
+}
+
+private val AmbientIndicationElementKey = ElementKey("AmbientIndication")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
new file mode 100644
index 0000000..db20f65
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -0,0 +1,217 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import android.view.View
+import android.widget.ImageView
+import androidx.annotation.IdRes
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.content.res.ResourcesCompat
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.animation.view.LaunchableImageView
+import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.Flow
+
+class BottomAreaSection
+@Inject
+constructor(
+ private val viewModel: KeyguardQuickAffordancesCombinedViewModel,
+ private val falsingManager: FalsingManager,
+ private val vibratorHelper: VibratorHelper,
+ private val indicationController: KeyguardIndicationController,
+ private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+) {
+ /**
+ * Renders a single lockscreen shortcut.
+ *
+ * @param isStart Whether the shortcut goes on the left (in left-to-right locales).
+ * @param applyPadding Whether to apply padding around the shortcut, this is needed if the
+ * shortcut is placed along the edges of the display.
+ */
+ @Composable
+ fun SceneScope.Shortcut(
+ isStart: Boolean,
+ applyPadding: Boolean,
+ modifier: Modifier = Modifier,
+ ) {
+ MovableElement(
+ key = if (isStart) StartButtonElementKey else EndButtonElementKey,
+ modifier = modifier,
+ ) {
+ Shortcut(
+ viewId = if (isStart) R.id.start_button else R.id.end_button,
+ viewModel = if (isStart) viewModel.startButton else viewModel.endButton,
+ transitionAlpha = viewModel.transitionAlpha,
+ falsingManager = falsingManager,
+ vibratorHelper = vibratorHelper,
+ indicationController = indicationController,
+ modifier =
+ if (applyPadding) {
+ Modifier.shortcutPadding()
+ } else {
+ Modifier
+ }
+ )
+ }
+ }
+
+ @Composable
+ fun SceneScope.IndicationArea(
+ modifier: Modifier = Modifier,
+ ) {
+ MovableElement(
+ key = IndicationAreaElementKey,
+ modifier = modifier.shortcutPadding(),
+ ) {
+ IndicationArea(
+ indicationAreaViewModel = indicationAreaViewModel,
+ keyguardRootViewModel = keyguardRootViewModel,
+ indicationController = indicationController,
+ )
+ }
+ }
+
+ @Composable
+ fun shortcutSizeDp(): DpSize {
+ return DpSize(
+ width = dimensionResource(R.dimen.keyguard_affordance_fixed_width),
+ height = dimensionResource(R.dimen.keyguard_affordance_fixed_height),
+ )
+ }
+
+ @Composable
+ private fun Shortcut(
+ @IdRes viewId: Int,
+ viewModel: Flow<KeyguardQuickAffordanceViewModel>,
+ transitionAlpha: Flow<Float>,
+ falsingManager: FalsingManager,
+ vibratorHelper: VibratorHelper,
+ indicationController: KeyguardIndicationController,
+ modifier: Modifier = Modifier,
+ ) {
+ val (binding, setBinding) = mutableStateOf<KeyguardQuickAffordanceViewBinder.Binding?>(null)
+
+ AndroidView(
+ factory = { context ->
+ val padding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(context, null).apply {
+ id = viewId
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+
+ setBinding(
+ KeyguardQuickAffordanceViewBinder.bind(
+ view,
+ viewModel,
+ transitionAlpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ )
+
+ view
+ },
+ onRelease = { binding?.destroy() },
+ modifier =
+ modifier.size(
+ width = shortcutSizeDp().width,
+ height = shortcutSizeDp().height,
+ )
+ )
+ }
+
+ @Composable
+ private fun IndicationArea(
+ indicationAreaViewModel: KeyguardIndicationAreaViewModel,
+ keyguardRootViewModel: KeyguardRootViewModel,
+ indicationController: KeyguardIndicationController,
+ modifier: Modifier = Modifier,
+ ) {
+ val (disposable, setDisposable) = mutableStateOf<DisposableHandle?>(null)
+
+ AndroidView(
+ factory = { context ->
+ val view = KeyguardIndicationArea(context, null)
+ setDisposable(
+ KeyguardIndicationAreaBinder.bind(
+ view = view,
+ viewModel = indicationAreaViewModel,
+ keyguardRootViewModel = keyguardRootViewModel,
+ indicationController = indicationController,
+ )
+ )
+ view
+ },
+ onRelease = { disposable?.dispose() },
+ modifier = modifier.fillMaxWidth(),
+ )
+ }
+
+ @Composable
+ private fun Modifier.shortcutPadding(): Modifier {
+ return this.padding(
+ horizontal = dimensionResource(R.dimen.keyguard_affordance_horizontal_offset)
+ )
+ .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset))
+ }
+}
+
+private val StartButtonElementKey = ElementKey("StartButton")
+private val EndButtonElementKey = ElementKey("EndButton")
+private val IndicationAreaElementKey = ElementKey("IndicationArea")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
new file mode 100644
index 0000000..eaf8063
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import javax.inject.Inject
+
+class ClockSection
+@Inject
+constructor(
+ private val viewModel: KeyguardClockViewModel,
+) {
+ @Composable
+ fun SceneScope.SmallClock(modifier: Modifier = Modifier) {
+ if (viewModel.useLargeClock) {
+ return
+ }
+
+ MovableElement(
+ key = ClockElementKey,
+ modifier = modifier,
+ ) {
+ Box(
+ modifier = Modifier.fillMaxWidth().background(Color.Magenta),
+ ) {
+ Text(
+ text = "TODO(b/316211368): Small clock",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+ }
+
+ @Composable
+ fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
+ if (!viewModel.useLargeClock) {
+ return
+ }
+
+ MovableElement(
+ key = ClockElementKey,
+ modifier = modifier,
+ ) {
+ Box(
+ modifier = Modifier.fillMaxWidth().background(Color.Blue),
+ ) {
+ Text(
+ text = "TODO(b/316211368): Large clock",
+ color = Color.White,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+ }
+}
+
+private val ClockElementKey = ElementKey("Clock")
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
new file mode 100644
index 0000000..2a6bea7
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -0,0 +1,177 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import android.content.Context
+import android.util.DisplayMetrics
+import android.view.WindowManager
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.keyguard.LockIconView
+import com.android.keyguard.LockIconViewController
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
+import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+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.plugins.FalsingManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import dagger.Lazy
+import javax.inject.Inject
+
+class LockSection
+@Inject
+constructor(
+ private val windowManager: WindowManager,
+ private val authController: AuthController,
+ private val featureFlags: FeatureFlagsClassic,
+ private val lockIconViewController: Lazy<LockIconViewController>,
+ private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
+ private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
+ private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
+ private val falsingManager: Lazy<FalsingManager>,
+ private val vibratorHelper: Lazy<VibratorHelper>,
+) {
+ @Composable
+ fun SceneScope.LockIcon(modifier: Modifier = Modifier) {
+ if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
+ return
+ }
+
+ 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(
+ this,
+ deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
+ falsingManager.get(),
+ vibratorHelper.get(),
+ )
+ }
+ } else {
+ // keyguardBottomAreaRefactor()
+ LockIconView(context, null).apply {
+ id = R.id.lock_icon_view
+ lockIconViewController.get().setLockIconView(this)
+ }
+ }
+ view
+ },
+ modifier =
+ modifier.element(LockIconElementKey).layout { measurable, _ ->
+ val lockIconBounds = lockIconBounds(context)
+ val placeable =
+ measurable.measure(
+ Constraints.fixed(
+ width = lockIconBounds.width,
+ height = lockIconBounds.height,
+ )
+ )
+ layout(
+ width = placeable.width,
+ height = placeable.height,
+ alignmentLines =
+ mapOf(
+ BlueprintAlignmentLines.LockIcon.Left to lockIconBounds.left,
+ BlueprintAlignmentLines.LockIcon.Top to lockIconBounds.top,
+ BlueprintAlignmentLines.LockIcon.Right to lockIconBounds.right,
+ BlueprintAlignmentLines.LockIcon.Bottom to lockIconBounds.bottom,
+ ),
+ ) {
+ placeable.place(0, 0)
+ }
+ },
+ )
+ }
+
+ /**
+ * Returns the bounds of the lock icon, in window view coordinates.
+ *
+ * On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are
+ * the same as the bounds of the sensor.
+ */
+ private fun lockIconBounds(
+ context: Context,
+ ): IntRect {
+ val windowViewBounds = windowManager.currentWindowMetrics.bounds
+ var widthPx = windowViewBounds.right.toFloat()
+ if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
+ val insets = windowManager.currentWindowMetrics.windowInsets
+ // Assumed to be initially neglected as there are no left or right insets in portrait.
+ // However, on landscape, these insets need to included when calculating the midpoint.
+ @Suppress("DEPRECATION")
+ widthPx -= (insets.systemWindowInsetLeft + insets.systemWindowInsetRight).toFloat()
+ }
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+
+ val udfpsLocation = authController.udfpsLocation
+ val (center, radius) =
+ if (authController.isUdfpsSupported && udfpsLocation != null) {
+ Pair(
+ IntOffset(
+ x = udfpsLocation.x,
+ y = udfpsLocation.y,
+ ),
+ authController.udfpsRadius.toInt(),
+ )
+ } else {
+ val scaleFactor = authController.scaleFactor
+ val bottomPaddingPx =
+ context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+ val heightPx = windowViewBounds.bottom.toFloat()
+
+ Pair(
+ IntOffset(
+ x = (widthPx / 2).toInt(),
+ y =
+ (heightPx - ((bottomPaddingPx + lockIconRadiusPx) * scaleFactor))
+ .toInt(),
+ ),
+ (lockIconRadiusPx * scaleFactor).toInt(),
+ )
+ }
+
+ return IntRect(center, radius)
+ }
+}
+
+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
new file mode 100644
index 0000000..c547e2b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+
+class NotificationSection
+@Inject
+constructor(
+ private val viewModel: NotificationsPlaceholderViewModel,
+) {
+ @Composable
+ fun SceneScope.Notifications(modifier: Modifier = Modifier) {
+ NotificationStack(
+ viewModel = viewModel,
+ isScrimVisible = false,
+ modifier = modifier,
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
new file mode 100644
index 0000000..44b0535
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.positionInParent
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
+import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.VibratorHelper
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+class SettingsMenuSection
+@Inject
+constructor(
+ private val viewModel: KeyguardSettingsMenuViewModel,
+ private val longPressViewModel: KeyguardLongPressViewModel,
+ private val vibratorHelper: VibratorHelper,
+ private val activityStarter: ActivityStarter,
+) {
+ @Composable
+ @SuppressWarnings("InflateParams") // null is passed into the inflate call, on purpose.
+ fun SettingsMenu(
+ onPlaced: (Rect?) -> Unit,
+ modifier: Modifier = Modifier,
+ ) {
+ val (disposableHandle, setDisposableHandle) =
+ remember { mutableStateOf<DisposableHandle?>(null) }
+ AndroidView(
+ factory = { context ->
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.keyguard_settings_popup_menu,
+ null,
+ )
+ .apply {
+ isVisible = false
+ alpha = 0f
+
+ setDisposableHandle(
+ KeyguardSettingsViewBinder.bind(
+ view = this,
+ viewModel = viewModel,
+ longPressViewModel = longPressViewModel,
+ rootViewModel = null,
+ vibratorHelper = vibratorHelper,
+ activityStarter = activityStarter,
+ )
+ )
+ }
+ },
+ onRelease = { disposableHandle?.dispose() },
+ modifier =
+ modifier
+ .padding(
+ bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset),
+ )
+ .padding(
+ horizontal =
+ dimensionResource(R.dimen.keyguard_affordance_horizontal_offset),
+ )
+ .onPlaced { coordinates ->
+ onPlaced(
+ if (!coordinates.size.toSize().isEmpty()) {
+ Rect(coordinates.positionInParent(), coordinates.size.toSize())
+ } else {
+ null
+ }
+ )
+ },
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
new file mode 100644
index 0000000..3c49cbc
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import android.widget.FrameLayout
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import javax.inject.Inject
+
+class SmartSpaceSection
+@Inject
+constructor(
+ private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+ private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
+ private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
+) {
+ @Composable
+ fun SceneScope.SmartSpace(modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier.element(SmartSpaceElementKey),
+ ) {
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
+ return
+ }
+
+ val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
+ val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+
+ if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.fillMaxWidth()
+ // All items will be constrained to be as tall as the shortest item.
+ .height(IntrinsicSize.Min)
+ .padding(
+ start = paddingBelowClockStart,
+ ),
+ ) {
+ Date()
+ Spacer(modifier = Modifier.width(4.dp))
+ Weather()
+ }
+ }
+
+ Card(
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(
+ start = paddingBelowClockStart,
+ end = paddingBelowClockEnd,
+ )
+ )
+ }
+ }
+
+ @Composable
+ private fun Card(
+ modifier: Modifier = Modifier,
+ ) {
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ addView(
+ lockscreenSmartspaceController.buildAndConnectView(this).apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ )
+
+ keyguardUnlockAnimationController.lockscreenSmartspace = this
+ }
+ )
+ }
+ },
+ onRelease = { keyguardUnlockAnimationController.lockscreenSmartspace = null },
+ modifier = modifier,
+ )
+ }
+
+ @Composable
+ private fun Weather(
+ modifier: Modifier = Modifier,
+ ) {
+ val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsState()
+ if (!isVisible) {
+ return
+ }
+
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ addView(
+ lockscreenSmartspaceController.buildAndConnectWeatherView(this).apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ )
+ }
+ )
+ }
+ },
+ modifier = modifier,
+ )
+ }
+
+ @Composable
+ private fun Date(
+ modifier: Modifier = Modifier,
+ ) {
+ val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsState()
+ if (!isVisible) {
+ return
+ }
+
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ addView(
+ lockscreenSmartspaceController.buildAndConnectDateView(this).apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ )
+ }
+ )
+ }
+ },
+ modifier = modifier,
+ )
+ }
+}
+
+private val SmartSpaceElementKey = ElementKey("SmartSpace")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
new file mode 100644
index 0000000..6811eb4
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/StatusBarSection.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.keyguard.ui.composable.section
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.height
+import com.android.keyguard.dagger.KeyguardStatusBarViewComponent
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.ShadeViewStateProvider
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView
+import com.android.systemui.util.Utils
+import dagger.Lazy
+import javax.inject.Inject
+
+class StatusBarSection
+@Inject
+constructor(
+ private val componentFactory: KeyguardStatusBarViewComponent.Factory,
+ private val notificationPanelView: Lazy<NotificationPanelView>,
+) {
+ @Composable
+ fun SceneScope.StatusBar(modifier: Modifier = Modifier) {
+ val context = LocalContext.current
+
+ MovableElement(
+ key = StatusBarElementKey,
+ modifier = modifier,
+ ) {
+ AndroidView(
+ factory = {
+ notificationPanelView.get().findViewById<View>(R.id.keyguard_header)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ }
+
+ val provider =
+ object : ShadeViewStateProvider {
+ override val lockscreenShadeDragProgress: Float = 0f
+ override val panelViewExpandedHeight: Float = 0f
+ override fun shouldHeadsUpBeVisible(): Boolean {
+ return false
+ }
+ }
+
+ @SuppressLint("InflateParams")
+ val view =
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.keyguard_status_bar,
+ null,
+ false,
+ ) as KeyguardStatusBarView
+ componentFactory.build(view, provider).keyguardStatusBarViewController.init()
+ view
+ },
+ modifier =
+ Modifier.fillMaxWidth().height {
+ Utils.getStatusBarHeaderHeightKeyguard(context)
+ },
+ )
+ }
+ }
+}
+
+private val StatusBarElementKey = ElementKey("StatusBar")
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 9d9b0a9..a85d9bf 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
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import android.graphics.Picture
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
@@ -66,12 +67,21 @@
* The movable content of this element, if this element is composed using
* [SceneScope.MovableElement].
*/
- val movableContent by
- // This is only accessed from the composition (main) thread, so no need to use the default
- // lock of lazy {} to synchronize.
- lazy(mode = LazyThreadSafetyMode.NONE) {
- movableContentOf { content: @Composable () -> Unit -> content() }
- }
+ private var _movableContent: (@Composable (@Composable () -> Unit) -> Unit)? = null
+ val movableContent: @Composable (@Composable () -> Unit) -> Unit
+ get() =
+ _movableContent
+ ?: movableContentOf { content: @Composable () -> Unit -> content() }
+ .also { _movableContent = it }
+
+ /**
+ * The [Picture] to which we save the last drawing commands of this element, if it is movable.
+ * This is necessary because the content of this element might not be composed in the scene it
+ * should currently be drawn.
+ */
+ private var _picture: Picture? = null
+ val picture: Picture
+ get() = _picture ?: Picture().also { _picture = it }
override fun toString(): String {
return "Element(key=$key)"
@@ -521,11 +531,6 @@
sceneValues.targetOffset = targetOffsetInScene
}
- // No need to place the element in this scene if we don't want to draw it anyways.
- if (!shouldDrawElement(layoutImpl, scene, element)) {
- return
- }
-
val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
val lastSharedValues = element.lastSharedValues
val lastValues = sceneValues.lastValues
@@ -548,6 +553,13 @@
lastSharedValues.offset = targetOffset
lastValues.offset = targetOffset
+ // No need to place the element in this scene if we don't want to draw it anyways. Note that
+ // it's still important to compute the target offset and update lastValues, otherwise it
+ // will be out of date.
+ if (!shouldDrawElement(layoutImpl, scene, element)) {
+ return
+ }
+
val offset = (targetOffset - currentOffset).round()
if (isElementOpaque(layoutImpl, element, scene, sceneValues)) {
// TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
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 306f276..49df2f6 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
@@ -16,7 +16,6 @@
package com.android.compose.animation.scene
-import android.graphics.Picture
import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
@@ -60,7 +59,7 @@
// The [Picture] to which we save the last drawing commands of this element. This is
// necessary because the content of this element might not be composed in this scene, in
// which case we still need to draw it.
- val picture = remember { Picture() }
+ val picture = element.picture
// Whether we should compose the movable element here. The scene picker logic to know in
// which scene we should compose/draw a movable element might depend on the current
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 6a7a3a0..30e50a9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -34,6 +34,7 @@
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
+import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
/** A scene in a [SceneTransitionLayout]. */
@Stable
@@ -152,4 +153,8 @@
bounds: ElementKey,
shape: Shape
): Modifier = punchHole(layoutImpl, element, bounds, shape)
+
+ override fun Modifier.noResizeDuringTransitions(): Modifier {
+ return noResizeDuringTransitions(layoutState = layoutImpl.state)
+ }
}
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 3608e37..5eb339e 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
@@ -65,11 +65,11 @@
SceneTransitionLayoutForTesting(
currentScene,
onChangeScene,
+ modifier,
transitions,
state,
edgeDetector,
transitionInterceptionThreshold,
- modifier,
onLayoutImpl = null,
scenes,
)
@@ -205,6 +205,12 @@
* the result.
*/
fun Modifier.punchHole(element: ElementKey, bounds: ElementKey, shape: Shape): Modifier
+
+ /**
+ * Don't resize during transitions. This can for instance be used to make sure that scrollable
+ * lists keep a constant size during transitions even if its elements are growing/shrinking.
+ */
+ fun Modifier.noResizeDuringTransitions(): Modifier
}
// TODO(b/291053742): Add animateSharedValueAsState(targetValue) without any ValueKey and ElementKey
@@ -257,12 +263,12 @@
internal fun SceneTransitionLayoutForTesting(
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
- transitions: SceneTransitions,
- state: SceneTransitionLayoutState,
- edgeDetector: EdgeDetector,
- transitionInterceptionThreshold: Float,
- modifier: Modifier,
- onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)?,
+ modifier: Modifier = Modifier,
+ transitions: SceneTransitions = transitions {},
+ state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
+ edgeDetector: EdgeDetector = DefaultEdgeDetector,
+ transitionInterceptionThreshold: Float = 0f,
+ onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null,
scenes: SceneTransitionLayoutScope.() -> Unit,
) {
val density = LocalDensity.current
@@ -280,6 +286,10 @@
.also { onLayoutImpl?.invoke(it) }
}
+ // TODO(b/317014852): Move this into the SideEffect {} again once STLImpl.scenes is not a
+ // SnapshotStateMap anymore.
+ layoutImpl.updateScenes(scenes)
+
val targetSceneChannel = remember { Channel<SceneKey>(Channel.CONFLATED) }
SideEffect {
if (state != layoutImpl.state) {
@@ -293,7 +303,6 @@
(state as SceneTransitionLayoutStateImpl).transitions = transitions
layoutImpl.density = density
layoutImpl.edgeDetector = edgeDetector
- layoutImpl.updateScenes(scenes)
state.transitions = transitions
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 c99c325..45e1a0f 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
@@ -46,7 +46,12 @@
builder: SceneTransitionLayoutScope.() -> Unit,
coroutineScope: CoroutineScope,
) {
- internal val scenes = mutableMapOf<SceneKey, Scene>()
+ /**
+ * The map of [Scene]s.
+ *
+ * TODO(b/317014852): Make this a normal MutableMap instead.
+ */
+ internal val scenes = SnapshotStateMap<SceneKey, Scene>()
/**
* The map of [Element]s.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt
new file mode 100644
index 0000000..bd36cb8
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/Size.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.compose.animation.scene.modifiers
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.unit.Constraints
+import com.android.compose.animation.scene.SceneTransitionLayoutState
+
+@OptIn(ExperimentalComposeUiApi::class)
+internal fun Modifier.noResizeDuringTransitions(layoutState: SceneTransitionLayoutState): Modifier {
+ return intermediateLayout { measurable, constraints ->
+ if (layoutState.currentTransition == null) {
+ return@intermediateLayout measurable.measure(constraints).run {
+ layout(width, height) { place(0, 0) }
+ }
+ }
+
+ // Make sure that this layout node has the same size than when we are at rest.
+ val sizeAtRest = lookaheadSize
+ measurable.measure(Constraints.fixed(sizeAtRest.width, sizeAtRest.height)).run {
+ layout(width, height) { place(0, 0) }
+ }
+ }
+}
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 d332910..da5a0a0 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
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
@@ -30,16 +31,21 @@
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.intermediateLayout
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.subjects.DpOffsetSubject
+import com.android.compose.test.subjects.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -259,11 +265,6 @@
SceneTransitionLayoutForTesting(
currentScene = currentScene,
onChangeScene = { currentScene = it },
- transitions = remember { transitions {} },
- state = remember { SceneTransitionLayoutState(currentScene) },
- edgeDetector = DefaultEdgeDetector,
- modifier = Modifier,
- transitionInterceptionThreshold = 0f,
onLayoutImpl = { nullableLayoutImpl = it },
) {
scene(TestScenes.SceneA) { /* Nothing */}
@@ -429,11 +430,6 @@
SceneTransitionLayoutForTesting(
currentScene = TestScenes.SceneA,
onChangeScene = {},
- transitions = remember { transitions {} },
- state = remember { SceneTransitionLayoutState(TestScenes.SceneA) },
- edgeDetector = DefaultEdgeDetector,
- modifier = Modifier,
- transitionInterceptionThreshold = 0f,
onLayoutImpl = { nullableLayoutImpl = it },
) {
scene(TestScenes.SceneA) { Box(Modifier.element(key)) }
@@ -484,11 +480,6 @@
SceneTransitionLayoutForTesting(
currentScene = TestScenes.SceneA,
onChangeScene = {},
- transitions = remember { transitions {} },
- state = remember { SceneTransitionLayoutState(TestScenes.SceneA) },
- edgeDetector = DefaultEdgeDetector,
- modifier = Modifier,
- transitionInterceptionThreshold = 0f,
onLayoutImpl = { nullableLayoutImpl = it },
) {
scene(TestScenes.SceneA) {
@@ -574,4 +565,86 @@
after { assertThat(fooCompositions).isEqualTo(1) }
}
}
+
+ @Test
+ fun sharedElementOffsetIsUpdatedEvenWhenNotPlaced() {
+ var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
+ var density: Density? = null
+
+ fun layoutImpl() = nullableLayoutImpl ?: error("nullableLayoutImpl was not set")
+
+ fun density() = density ?: error("density was not set")
+
+ fun Offset.toDpOffset() = with(density()) { DpOffset(x.toDp(), y.toDp()) }
+
+ fun foo() = layoutImpl().elements[TestElements.Foo] ?: error("Foo not in elements map")
+
+ fun Element.lastSharedOffset() = lastSharedValues.offset.toDpOffset()
+
+ fun Element.lastOffsetIn(scene: SceneKey) =
+ (sceneValues[scene] ?: error("$scene not in sceneValues map"))
+ .lastValues
+ .offset
+ .toDpOffset()
+
+ rule.testTransition(
+ from = TestScenes.SceneA,
+ to = TestScenes.SceneB,
+ transitionLayout = { currentScene, onChangeScene ->
+ density = LocalDensity.current
+
+ SceneTransitionLayoutForTesting(
+ currentScene = currentScene,
+ onChangeScene = onChangeScene,
+ onLayoutImpl = { nullableLayoutImpl = it },
+ transitions =
+ transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(durationMillis = 4 * 16, easing = LinearEasing)
+ }
+ }
+ ) {
+ scene(TestScenes.SceneA) { Box(Modifier.element(TestElements.Foo)) }
+ scene(TestScenes.SceneB) {
+ Box(Modifier.offset(x = 40.dp, y = 80.dp).element(TestElements.Foo))
+ }
+ }
+ }
+ ) {
+ val tolerance = DpOffsetSubject.DefaultTolerance
+
+ before {
+ val expected = DpOffset(0.dp, 0.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ }
+
+ at(16) {
+ val expected = DpOffset(10.dp, 20.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+
+ at(32) {
+ val expected = DpOffset(20.dp, 40.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+
+ at(48) {
+ val expected = DpOffset(30.dp, 60.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneA)).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+
+ after {
+ val expected = DpOffset(40.dp, 80.dp)
+ assertThat(foo().lastSharedOffset()).isWithin(tolerance).of(expected)
+ assertThat(foo().lastOffsetIn(TestScenes.SceneB)).isWithin(tolerance).of(expected)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeTest.kt
new file mode 100644
index 0000000..2c159d1
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeTest.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.compose.animation.scene.modifiers
+
+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.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.element
+import com.android.compose.animation.scene.testTransition
+import com.android.compose.test.assertSizeIsEqualTo
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SizeTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun noResizeDuringTransitions() {
+ // The tag for the parent of the shared Foo element.
+ val parentTag = "parent"
+
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.element(TestElements.Foo).size(100.dp)) },
+ toSceneContent = {
+ // Don't resize the parent of Foo during transitions so that it's always the same
+ // size as when there is no transition (200dp).
+ Box(Modifier.noResizeDuringTransitions().testTag(parentTag)) {
+ Box(Modifier.element(TestElements.Foo).size(200.dp))
+ }
+ },
+ transition = { spec = tween(durationMillis = 4 * 16, easing = LinearEasing) },
+ ) {
+ at(16) { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ at(32) { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ at(48) { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ after { rule.onNodeWithTag(parentTag).assertSizeIsEqualTo(200.dp, 200.dp) }
+ }
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index b9d6643..2bfa7d9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -33,6 +33,8 @@
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.TextAnimator
import com.android.systemui.customization.R
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.Logger
import com.android.systemui.log.core.MessageBuffer
import java.io.PrintWriter
@@ -51,12 +53,13 @@
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- var messageBuffer: MessageBuffer? = null
- set(value) {
- logger = if (value != null) Logger(value, TAG) else null
- }
-
- private var logger: Logger? = null
+ // To protect us from issues from this being null while the TextView constructor is running, we
+ // implement the get method and ensure a value is returned before initialization is complete.
+ private var logger = DEFAULT_LOGGER
+ get() = field ?: DEFAULT_LOGGER
+ var messageBuffer: MessageBuffer
+ get() = logger.buffer
+ set(value) { logger = Logger(value, TAG) }
private val time = Calendar.getInstance()
@@ -133,8 +136,8 @@
}
override fun onAttachedToWindow() {
+ logger.d("onAttachedToWindow")
super.onAttachedToWindow()
- logger?.d("onAttachedToWindow")
refreshFormat()
}
@@ -150,13 +153,13 @@
time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
contentDescription = DateFormat.format(descFormat, time)
val formattedText = DateFormat.format(format, time)
- logger?.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
+ logger.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
// Setting text actually triggers a layout pass (because the text view is set to
// wrap_content width and TextView always relayouts for this). Avoid needless
// relayout if the text didn't actually change.
if (!TextUtils.equals(text, formattedText)) {
text = formattedText
- logger?.d({ "refreshTime: done setting new time text to: $str1" }) {
+ logger.d({ "refreshTime: done setting new time text to: $str1" }) {
str1 = formattedText?.toString()
}
// Because the TextLayout may mutate under the hood as a result of the new text, we
@@ -165,21 +168,22 @@
// without being notified TextInterpolator being notified.
if (layout != null) {
textAnimator?.updateLayout(layout)
- logger?.d("refreshTime: done updating textAnimator layout")
+ logger.d("refreshTime: done updating textAnimator layout")
}
requestLayout()
- logger?.d("refreshTime: after requestLayout")
+ logger.d("refreshTime: after requestLayout")
}
}
fun onTimeZoneChanged(timeZone: TimeZone?) {
+ logger.d({ "onTimeZoneChanged($str1)" }) { str1 = timeZone?.toString() }
time.timeZone = timeZone
refreshFormat()
- logger?.d({ "onTimeZoneChanged newTimeZone=$str1" }) { str1 = timeZone?.toString() }
}
@SuppressLint("DrawAllocation")
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ logger.d("onMeasure")
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val animator = textAnimator
if (animator == null) {
@@ -189,10 +193,10 @@
} else {
animator.updateLayout(layout)
}
- logger?.d("onMeasure")
}
override fun onDraw(canvas: Canvas) {
+ logger.d({ "onDraw($str1)"}) { str1 = text.toString() }
// Use textAnimator to render text if animation is enabled.
// Otherwise default to using standard draw functions.
if (isAnimationEnabled) {
@@ -201,22 +205,23 @@
} else {
super.onDraw(canvas)
}
- logger?.d("onDraw")
}
override fun invalidate() {
+ @Suppress("UNNECESSARY_SAFE_CALL")
+ // logger won't be initialized when called by TextView's constructor
+ logger.d("invalidate")
super.invalidate()
- logger?.d("invalidate")
}
override fun onTextChanged(
- text: CharSequence,
- start: Int,
- lengthBefore: Int,
- lengthAfter: Int
+ text: CharSequence,
+ start: Int,
+ lengthBefore: Int,
+ lengthAfter: Int
) {
+ logger.d({ "onTextChanged($str1)" }) { str1 = text.toString() }
super.onTextChanged(text, start, lengthBefore, lengthAfter)
- logger?.d({ "onTextChanged text=$str1" }) { str1 = text.toString() }
}
fun setLineSpacingScale(scale: Float) {
@@ -230,7 +235,7 @@
}
fun animateColorChange() {
- logger?.d("animateColorChange")
+ logger.d("animateColorChange")
setTextStyle(
weight = lockScreenWeight,
textSize = -1f,
@@ -252,7 +257,7 @@
}
fun animateAppearOnLockscreen() {
- logger?.d("animateAppearOnLockscreen")
+ logger.d("animateAppearOnLockscreen")
setTextStyle(
weight = dozingWeight,
textSize = -1f,
@@ -278,7 +283,7 @@
if (isAnimationEnabled && textAnimator == null) {
return
}
- logger?.d("animateFoldAppear")
+ logger.d("animateFoldAppear")
setTextStyle(
weight = lockScreenWeightInternal,
textSize = -1f,
@@ -305,7 +310,7 @@
// Skip charge animation if dozing animation is already playing.
return
}
- logger?.d("animateCharge")
+ logger.d("animateCharge")
val startAnimPhase2 = Runnable {
setTextStyle(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
@@ -329,7 +334,7 @@
}
fun animateDoze(isDozing: Boolean, animate: Boolean) {
- logger?.d("animateDoze")
+ logger.d("animateDoze")
setTextStyle(
weight = if (isDozing) dozingWeight else lockScreenWeight,
textSize = -1f,
@@ -448,7 +453,7 @@
isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
else -> DOUBLE_LINE_FORMAT_12_HOUR
}
- logger?.d({ "refreshFormat format=$str1" }) { str1 = format?.toString() }
+ logger.d({ "refreshFormat($str1)" }) { str1 = format?.toString() }
descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
refreshTime()
@@ -552,6 +557,8 @@
companion object {
private val TAG = AnimatableClockView::class.simpleName!!
+ private val DEFAULT_LOGGER = Logger(LogcatOnlyMessageBuffer(LogLevel.WARNING), TAG)
+
const val ANIMATION_DURATION_FOLD_TO_AOD: Int = 600
private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index cdd074d..41bde52 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -21,20 +21,16 @@
import android.net.Uri
import android.os.UserHandle
import android.provider.Settings
-import android.util.Log
import androidx.annotation.OpenForTesting
-import com.android.systemui.log.LogMessageImpl
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.log.core.Logger
-import com.android.systemui.log.core.MessageBuffer
-import com.android.systemui.log.core.MessageInitializer
-import com.android.systemui.log.core.MessagePrinter
import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockProviderPlugin
@@ -77,32 +73,6 @@
return result ?: value
}
-private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() }
-
-private inline fun Logger?.tryLog(
- tag: String,
- level: LogLevel,
- messageInitializer: MessageInitializer,
- noinline messagePrinter: MessagePrinter,
- ex: Throwable? = null,
-) {
- if (this != null) {
- // Wrap messagePrinter to convert it from crossinline to noinline
- this.log(level, messagePrinter, ex, messageInitializer)
- } else {
- messageInitializer(TMP_MESSAGE)
- val msg = messagePrinter(TMP_MESSAGE)
- when (level) {
- LogLevel.VERBOSE -> Log.v(tag, msg, ex)
- LogLevel.DEBUG -> Log.d(tag, msg, ex)
- LogLevel.INFO -> Log.i(tag, msg, ex)
- LogLevel.WARNING -> Log.w(tag, msg, ex)
- LogLevel.ERROR -> Log.e(tag, msg, ex)
- LogLevel.WTF -> Log.wtf(tag, msg, ex)
- }
- }
-}
-
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
val context: Context,
@@ -114,12 +84,15 @@
val handleAllUsers: Boolean,
defaultClockProvider: ClockProvider,
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
- messageBuffer: MessageBuffer? = null,
+ val clockBuffers: ClockMessageBuffers? = null,
val keepAllLoaded: Boolean,
subTag: String,
var isTransitClockEnabled: Boolean = false,
) {
private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
+ private val logger: Logger =
+ Logger(clockBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.DEBUG), TAG)
+
interface ClockChangeListener {
// Called when the active clock changes
fun onCurrentClockChanged() {}
@@ -128,7 +101,6 @@
fun onAvailableClocksChanged() {}
}
- private val logger: Logger? = if (messageBuffer != null) Logger(messageBuffer, TAG) else null
private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver =
@@ -157,21 +129,15 @@
val knownClocks = KNOWN_PLUGINS.get(manager.getPackage())
if (knownClocks == null) {
- logger.tryLog(
- TAG,
- LogLevel.WARNING,
- { str1 = manager.getPackage() },
- { "Loading unrecognized clock package: $str1" }
- )
+ logger.w({ "Loading unrecognized clock package: $str1" }) {
+ str1 = manager.getPackage()
+ }
return true
}
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- { str1 = manager.getPackage() },
- { "Skipping initial load of known clock package package: $str1" }
- )
+ logger.i({ "Skipping initial load of known clock package package: $str1" }) {
+ str1 = manager.getPackage()
+ }
var isCurrentClock = false
var isClockListChanged = false
@@ -185,19 +151,14 @@
}
if (manager != info.manager) {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- {
- str1 = id
- str2 = info.manager.toString()
- str3 = manager.toString()
- },
- {
- "Clock Id conflict on attach: " +
- "$str1 is double registered by $str2 and $str3"
- }
- )
+ logger.e({
+ "Clock Id conflict on attach: " +
+ "$str1 is double registered by $str2 and $str3"
+ }) {
+ str1 = id
+ str2 = info.manager.toString()
+ str3 = manager.toString()
+ }
continue
}
@@ -219,6 +180,8 @@
pluginContext: Context,
manager: PluginLifecycleManager<ClockProviderPlugin>
) {
+ plugin.initialize(clockBuffers)
+
var isClockListChanged = false
for (clock in plugin.getClocks()) {
val id = clock.clockId
@@ -233,19 +196,14 @@
}
if (manager != info.manager) {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- {
- str1 = id
- str2 = info.manager.toString()
- str3 = manager.toString()
- },
- {
- "Clock Id conflict on load: " +
- "$str1 is double registered by $str2 and $str3"
- }
- )
+ logger.e({
+ "Clock Id conflict on load: " +
+ "$str1 is double registered by $str2 and $str3"
+ }) {
+ str1 = id
+ str2 = info.manager.toString()
+ str3 = manager.toString()
+ }
manager.unloadPlugin()
continue
}
@@ -268,19 +226,14 @@
val id = clock.clockId
val info = availableClocks[id]
if (info?.manager != manager) {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- {
- str1 = id
- str2 = info?.manager.toString()
- str3 = manager.toString()
- },
- {
- "Clock Id conflict on unload: " +
- "$str1 is double registered by $str2 and $str3"
- }
- )
+ logger.e({
+ "Clock Id conflict on unload: " +
+ "$str1 is double registered by $str2 and $str3"
+ }) {
+ str1 = id
+ str2 = info?.manager.toString()
+ str3 = manager.toString()
+ }
continue
}
info.provider = null
@@ -350,7 +303,7 @@
ClockSettings.deserialize(json)
} catch (ex: Exception) {
- logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
+ logger.e("Failed to parse clock settings", ex)
null
}
settings = result
@@ -379,7 +332,7 @@
)
}
} catch (ex: Exception) {
- logger.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
+ logger.e("Failed to set clock settings", ex)
}
settings = value
}
@@ -451,7 +404,8 @@
}
init {
- // Register default clock designs
+ // Initialize & register default clock designs
+ defaultClockProvider.initialize(clockBuffers)
for (clock in defaultClockProvider.getClocks()) {
availableClocks[clock.clockId] = ClockInfo(clock, defaultClockProvider, null)
}
@@ -514,12 +468,7 @@
fun verifyLoadedProviders() {
val shouldSchedule = isQueued.compareAndSet(false, true)
if (!shouldSchedule) {
- logger.tryLog(
- TAG,
- LogLevel.VERBOSE,
- {},
- { "verifyLoadedProviders: shouldSchedule=false" }
- )
+ logger.v("verifyLoadedProviders: shouldSchedule=false")
return
}
@@ -528,12 +477,7 @@
synchronized(availableClocks) {
isQueued.set(false)
if (keepAllLoaded) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: keepAllLoaded=true" }
- )
+ logger.i("verifyLoadedProviders: keepAllLoaded=true")
// Enforce that all plugins are loaded if requested
for ((_, info) in availableClocks) {
info.manager?.loadPlugin()
@@ -543,12 +487,7 @@
val currentClock = availableClocks[currentClockId]
if (currentClock == null) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: currentClock=null" }
- )
+ logger.i("verifyLoadedProviders: currentClock=null")
// Current Clock missing, load no plugins and use default
for ((_, info) in availableClocks) {
info.manager?.unloadPlugin()
@@ -556,12 +495,7 @@
return@launch
}
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: load currentClock" }
- )
+ logger.i("verifyLoadedProviders: load currentClock")
val currentManager = currentClock.manager
currentManager?.loadPlugin()
@@ -577,30 +511,26 @@
private fun onConnected(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Connected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
}
private fun onLoaded(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Loaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
if (isCurrent) {
triggerOnCurrentClockChanged()
@@ -609,16 +539,14 @@
private fun onUnloaded(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.WARNING else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Unloaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
if (isCurrent) {
triggerOnCurrentClockChanged()
@@ -627,16 +555,14 @@
private fun onDisconnected(info: ClockInfo) {
val isCurrent = currentClockId == info.metadata.clockId
- logger.tryLog(
- TAG,
+ logger.log(
if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
- {
- str1 = info.metadata.clockId
- str2 = info.manager.toString()
- bool1 = isCurrent
- },
{ "Disconnected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
- )
+ ) {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ }
}
fun getClocks(): List<ClockMetadata> {
@@ -676,23 +602,13 @@
if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId)
if (clock != null) {
- logger.tryLog(TAG, LogLevel.INFO, { str1 = clockId }, { "Rendering clock $str1" })
+ logger.i({ "Rendering clock $str1" }) { str1 = clockId }
return clock
} else if (availableClocks.containsKey(clockId)) {
- logger.tryLog(
- TAG,
- LogLevel.WARNING,
- { str1 = clockId },
- { "Clock $str1 not loaded; using default" }
- )
+ logger.w({ "Clock $str1 not loaded; using default" }) { str1 = clockId }
verifyLoadedProviders()
} else {
- logger.tryLog(
- TAG,
- LogLevel.ERROR,
- { str1 = clockId },
- { "Clock $str1 not found; using default" }
- )
+ logger.e({ "Clock $str1 not found; using default" }) { str1 = clockId }
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 01c03b1..99d3216 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
import com.android.systemui.plugins.clocks.WeatherData
@@ -41,8 +42,6 @@
import java.util.Locale
import java.util.TimeZone
-private val TAG = DefaultClockController::class.simpleName
-
/**
* Controls the default clock visuals.
*
@@ -56,6 +55,7 @@
private val settings: ClockSettings?,
private val hasStepClockAnimation: Boolean = false,
private val migratedClocks: Boolean = false,
+ messageBuffers: ClockMessageBuffers? = null,
) : ClockController {
override val smallClock: DefaultClockFaceController
override val largeClock: LargeClockFaceController
@@ -83,13 +83,15 @@
DefaultClockFaceController(
layoutInflater.inflate(R.layout.clock_default_small, parent, false)
as AnimatableClockView,
- settings?.seedColor
+ settings?.seedColor,
+ messageBuffers?.smallClockMessageBuffer
)
largeClock =
LargeClockFaceController(
layoutInflater.inflate(R.layout.clock_default_large, parent, false)
as AnimatableClockView,
- settings?.seedColor
+ settings?.seedColor,
+ messageBuffers?.largeClockMessageBuffer
)
clocks = listOf(smallClock.view, largeClock.view)
@@ -110,6 +112,7 @@
open inner class DefaultClockFaceController(
override val view: AnimatableClockView,
var seedColor: Int?,
+ messageBuffer: MessageBuffer?,
) : ClockFaceController {
// MAGENTA is a placeholder, and will be assigned correctly in initialize
@@ -120,12 +123,6 @@
override val config = ClockFaceConfig()
override val layout = DefaultClockFaceLayout(view)
- override var messageBuffer: MessageBuffer?
- get() = view.messageBuffer
- set(value) {
- view.messageBuffer = value
- }
-
override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
internal set
@@ -134,6 +131,7 @@
currentColor = seedColor!!
}
view.setColors(DOZE_COLOR, currentColor)
+ messageBuffer?.let { view.messageBuffer = it }
}
override val events =
@@ -188,7 +186,8 @@
inner class LargeClockFaceController(
view: AnimatableClockView,
seedColor: Int?,
- ) : DefaultClockFaceController(view, seedColor) {
+ messageBuffer: MessageBuffer?,
+ ) : DefaultClockFaceController(view, seedColor, messageBuffer) {
override val layout = DefaultClockFaceLayout(view)
override val config =
ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index a219be5..20f87a0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -20,6 +20,7 @@
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockSettings
@@ -35,6 +36,12 @@
val hasStepClockAnimation: Boolean = false,
val migratedClocks: Boolean = false
) : ClockProvider {
+ private var messageBuffers: ClockMessageBuffers? = null
+
+ override fun initialize(buffers: ClockMessageBuffers?) {
+ messageBuffers = buffers
+ }
+
override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID))
override fun createClock(settings: ClockSettings): ClockController {
@@ -49,6 +56,7 @@
settings,
hasStepClockAnimation,
migratedClocks,
+ messageBuffers,
)
}
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 0b7c3f9..4b21105 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
@@ -17,56 +17,39 @@
package com.android.systemui.shared.notifications.data.repository
import android.provider.Settings
-import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel
import com.android.systemui.shared.settings.data.repository.SecureSettingsRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
-/** Provides access to state related to notifications. */
+/** Provides access to state related to notification settings. */
class NotificationSettingsRepository(
scope: CoroutineScope,
private val backgroundDispatcher: CoroutineDispatcher,
private val secureSettingsRepository: SecureSettingsRepository,
) {
/** The current state of the notification setting. */
- val settings: SharedFlow<NotificationSettingsModel> =
+ val isShowNotificationsOnLockScreenEnabled: StateFlow<Boolean> =
secureSettingsRepository
.intSetting(
name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
)
- .map { lockScreenShowNotificationsInt ->
- NotificationSettingsModel(
- isShowNotificationsOnLockScreenEnabled = lockScreenShowNotificationsInt == 1,
- )
- }
- .shareIn(
+ .map { it == 1 }
+ .stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(),
- replay = 1,
+ initialValue = false,
)
- suspend fun getSettings(): NotificationSettingsModel {
- return withContext(backgroundDispatcher) {
- NotificationSettingsModel(
- isShowNotificationsOnLockScreenEnabled =
- secureSettingsRepository.get(
- name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- defaultValue = 0,
- ) == 1
- )
- }
- }
-
- suspend fun setSettings(model: NotificationSettingsModel) {
+ suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
withContext(backgroundDispatcher) {
- secureSettingsRepository.set(
+ secureSettingsRepository.setInt(
name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- value = if (model.isShowNotificationsOnLockScreenEnabled) 1 else 0,
+ value = if (enabled) 1 else 0,
)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
index 21f3aca..9ec6ec8 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
@@ -17,32 +17,23 @@
package com.android.systemui.shared.notifications.domain.interactor
import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
-import com.android.systemui.shared.notifications.shared.model.NotificationSettingsModel
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/** Encapsulates business logic for interacting with notification settings. */
class NotificationSettingsInteractor(
private val repository: NotificationSettingsRepository,
) {
- /** The current state of the notification setting. */
- val settings: Flow<NotificationSettingsModel> = repository.settings
+ /** Should notifications be visible on the lockscreen? */
+ val isShowNotificationsOnLockScreenEnabled: StateFlow<Boolean> =
+ repository.isShowNotificationsOnLockScreenEnabled
+
+ suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
+ repository.setShowNotificationsOnLockscreenEnabled(enabled)
+ }
/** Toggles the setting to show or hide notifications on the lock screen. */
- suspend fun toggleShowNotificationsOnLockScreenEnabled() {
- val currentModel = repository.getSettings()
- setSettings(
- currentModel.copy(
- isShowNotificationsOnLockScreenEnabled =
- !currentModel.isShowNotificationsOnLockScreenEnabled,
- )
- )
- }
-
- suspend fun setSettings(model: NotificationSettingsModel) {
- repository.setSettings(model)
- }
-
- suspend fun getSettings(): NotificationSettingsModel {
- return repository.getSettings()
+ suspend fun toggleShowNotificationsOnLockscreenEnabled() {
+ val current = repository.isShowNotificationsOnLockScreenEnabled.value
+ repository.setShowNotificationsOnLockscreenEnabled(!current)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt
deleted file mode 100644
index 7e35360..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/shared/model/NotificationSettingsModel.kt
+++ /dev/null
@@ -1,24 +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.shared.notifications.shared.model
-
-/** Models notification settings. */
-data class NotificationSettingsModel(
- /** Whether notifications are shown on the lock screen. */
- val isShowNotificationsOnLockScreenEnabled: Boolean = false,
-)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
index 7ef16a8..754d5dc 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/settings/data/repository/SecureSettingsRepository.kt
@@ -37,15 +37,17 @@
): Flow<Int>
/** Updates the value of the setting with the given name. */
- suspend fun set(
+ suspend fun setInt(
name: String,
value: Int,
)
- suspend fun get(
+ suspend fun getInt(
name: String,
defaultValue: Int = 0,
): Int
+
+ suspend fun getString(name: String): String?
}
class SecureSettingsRepositoryImpl(
@@ -80,7 +82,7 @@
.flowOn(backgroundDispatcher)
}
- override suspend fun set(name: String, value: Int) {
+ override suspend fun setInt(name: String, value: Int) {
withContext(backgroundDispatcher) {
Settings.Secure.putInt(
contentResolver,
@@ -90,7 +92,7 @@
}
}
- override suspend fun get(name: String, defaultValue: Int): Int {
+ override suspend fun getInt(name: String, defaultValue: Int): Int {
return withContext(backgroundDispatcher) {
Settings.Secure.getInt(
contentResolver,
@@ -99,4 +101,13 @@
)
}
}
+
+ override suspend fun getString(name: String): String? {
+ return withContext(backgroundDispatcher) {
+ Settings.Secure.getString(
+ contentResolver,
+ name,
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
index 1c86a07..37b9792 100644
--- a/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
+++ b/packages/SystemUI/customization/tests/utils/src/com/android/systemui/shared/settings/data/repository/FakeSecureSettingsRepository.kt
@@ -28,11 +28,15 @@
return settings.map { it.getOrDefault(name, defaultValue.toString()) }.map { it.toInt() }
}
- override suspend fun set(name: String, value: Int) {
+ override suspend fun setInt(name: String, value: Int) {
settings.value = settings.value.toMutableMap().apply { this[name] = value.toString() }
}
- override suspend fun get(name: String, defaultValue: Int): Int {
+ override suspend fun getInt(name: String, defaultValue: Int): Int {
return settings.value[name]?.toInt() ?: defaultValue
}
+
+ override suspend fun getString(name: String): String? {
+ return settings.value[name]
+ }
}
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt
new file mode 100644
index 0000000..006b521
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogcatOnlyMessageBuffer.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.log.core
+
+import android.util.Log
+import com.android.systemui.log.LogMessageImpl
+
+/**
+ * A simple implementation of [MessageBuffer] that forwards messages to [android.util.Log]
+ * immediately. This defeats the intention behind [LogBuffer] and should only be used when
+ * [LogBuffer]s are unavailable in a certain context.
+ */
+class LogcatOnlyMessageBuffer(
+ val targetLogLevel: LogLevel,
+) : MessageBuffer {
+ private val singleMessage = LogMessageImpl.Factory.create()
+ private var isObtained: Boolean = false
+
+ @Synchronized
+ override fun obtain(
+ tag: String,
+ level: LogLevel,
+ messagePrinter: MessagePrinter,
+ exception: Throwable?,
+ ): LogMessage {
+ if (isObtained) {
+ throw UnsupportedOperationException(
+ "Message has already been obtained. Call order is incorrect."
+ )
+ }
+
+ singleMessage.reset(tag, level, System.currentTimeMillis(), messagePrinter, exception)
+ isObtained = true
+ return singleMessage
+ }
+
+ @Synchronized
+ override fun commit(message: LogMessage) {
+ if (singleMessage != message) {
+ throw IllegalArgumentException("Message argument is not the expected message.")
+ }
+ if (!isObtained) {
+ throw UnsupportedOperationException(
+ "Message has not been obtained. Call order is incorrect."
+ )
+ }
+
+ if (message.level >= targetLogLevel) {
+ val strMessage = message.messagePrinter(message)
+ when (message.level) {
+ LogLevel.VERBOSE -> Log.v(message.tag, strMessage, message.exception)
+ LogLevel.DEBUG -> Log.d(message.tag, strMessage, message.exception)
+ LogLevel.INFO -> Log.i(message.tag, strMessage, message.exception)
+ LogLevel.WARNING -> Log.w(message.tag, strMessage, message.exception)
+ LogLevel.ERROR -> Log.e(message.tag, strMessage, message.exception)
+ LogLevel.WTF -> Log.wtf(message.tag, strMessage, message.exception)
+ }
+ }
+
+ isObtained = false
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 3fbcf6d..1519021 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -24,6 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -90,8 +91,8 @@
.thenReturn(mock(ImageView::class.java))
`when`(keyguardPasswordView.resources).thenReturn(context.resources)
val fakeFeatureFlags = FakeFeatureFlags()
- fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+ mSetFlagsRule.enableFlags(AconfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
keyguardPasswordViewController =
KeyguardPasswordViewController(
keyguardPasswordView,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 74c9225..e2bdc49 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,6 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -75,8 +76,7 @@
private lateinit var mKeyguardMessageAreaControllerFactory:
KeyguardMessageAreaController.Factory
- @Mock
- private lateinit var mSelectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var mKeyguardMessageAreaController:
@@ -95,7 +95,6 @@
whenever(mKeyguardMessageAreaControllerFactory.create(any()))
.thenReturn(mKeyguardMessageAreaController)
fakeFeatureFlags = FakeFeatureFlags()
- fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
mKeyguardPatternView =
View.inflate(mContext, R.layout.keyguard_pattern_view, null) as KeyguardPatternView
@@ -166,7 +165,7 @@
@Test
fun withFeatureFlagOn_oldMessage_isHidden() {
- fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
mKeyguardPatternViewController.onViewAttached()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index d41c249..e893169 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -35,7 +35,6 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -103,8 +102,7 @@
when(mPinBasedInputView.getResources()).thenReturn(getContext().getResources());
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
- featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
-
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index f170135..1fc2843 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -36,6 +36,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate
import com.android.systemui.biometrics.SideFpsController
@@ -196,12 +197,12 @@
whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(true)
featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false)
featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
featureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
keyguardPasswordViewController =
KeyguardPasswordViewController(
keyguardPasswordView,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 84d73543..cbcca55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -24,10 +24,10 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
@@ -81,8 +81,8 @@
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
as KeyguardSimPinView
val fakeFeatureFlags = FakeFeatureFlags()
- fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
+ mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
underTest =
KeyguardSimPinViewController(
simPinView,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 7b1f302..45a60199 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -24,10 +24,10 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
@@ -75,8 +75,7 @@
LayoutInflater.from(context).inflate(R.layout.keyguard_sim_puk_view, null)
as KeyguardSimPukView
val fakeFeatureFlags = FakeFeatureFlags()
- fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
-
+ mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
underTest =
KeyguardSimPukViewController(
simPukView,
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 64ddbc7..b9759cc 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
@@ -35,8 +35,10 @@
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.function.Function
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runCurrent
@@ -55,9 +57,11 @@
@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 testUtils = SceneTestUtils(this)
private val testScope = testUtils.testScope
+ private val clock = FakeSystemClock()
private val userRepository = FakeUserRepository()
private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
@@ -78,10 +82,13 @@
underTest =
AuthenticationRepositoryImpl(
applicationScope = testScope.backgroundScope,
- getSecurityMode = getSecurityMode,
backgroundDispatcher = testUtils.testDispatcher,
+ flags = testUtils.sceneContainerFlags,
+ clock = clock,
+ getSecurityMode = getSecurityMode,
userRepository = userRepository,
lockPatternUtils = lockPatternUtils,
+ devicePolicyManager = devicePolicyManager,
broadcastDispatcher = fakeBroadcastDispatcher,
mobileConnectionsRepository = mobileConnectionsRepository,
)
@@ -141,22 +148,6 @@
}
@Test
- fun reportAuthenticationAttempt_emitsAuthenticationChallengeResult() =
- testScope.runTest {
- val authenticationChallengeResults by
- collectValues(underTest.authenticationChallengeResult)
-
- runCurrent()
- underTest.reportAuthenticationAttempt(true)
- runCurrent()
- underTest.reportAuthenticationAttempt(false)
- runCurrent()
- underTest.reportAuthenticationAttempt(true)
-
- assertThat(authenticationChallengeResults).isEqualTo(listOf(true, false, true))
- }
-
- @Test
fun isPinEnhancedPrivacyEnabled() =
testScope.runTest {
whenever(lockPatternUtils.isPinEnhancedPrivacyEnabled(USER_INFOS[0].id))
@@ -172,6 +163,45 @@
assertThat(values.last()).isTrue()
}
+ @Test
+ fun lockoutEndTimestamp() =
+ testScope.runTest {
+ val lockoutEndMs = clock.elapsedRealtime() + 30.seconds.inWholeMilliseconds
+ whenever(lockPatternUtils.getLockoutAttemptDeadline(USER_INFOS[0].id))
+ .thenReturn(lockoutEndMs)
+ whenever(lockPatternUtils.getLockoutAttemptDeadline(USER_INFOS[1].id)).thenReturn(0)
+
+ // Switch to a user who is not locked-out.
+ userRepository.setSelectedUserInfo(USER_INFOS[1])
+ assertThat(underTest.lockoutEndTimestamp).isNull()
+
+ // Switch back to the locked-out user, verify the timestamp is up-to-date.
+ userRepository.setSelectedUserInfo(USER_INFOS[0])
+ assertThat(underTest.lockoutEndTimestamp).isEqualTo(lockoutEndMs)
+
+ // After the lockout expires, null is returned.
+ clock.setElapsedRealtime(lockoutEndMs)
+ assertThat(underTest.lockoutEndTimestamp).isNull()
+ }
+
+ @Test
+ fun hasLockoutOccurred() =
+ testScope.runTest {
+ val hasLockoutOccurred by collectLastValue(underTest.hasLockoutOccurred)
+ assertThat(hasLockoutOccurred).isFalse()
+
+ underTest.reportLockoutStarted(1000)
+ assertThat(hasLockoutOccurred).isTrue()
+
+ clock.setElapsedRealtime(clock.elapsedRealtime() + 60.seconds.inWholeMilliseconds)
+
+ underTest.reportAuthenticationAttempt(isSuccessful = false)
+ assertThat(hasLockoutOccurred).isTrue()
+
+ underTest.reportAuthenticationAttempt(isSuccessful = true)
+ assertThat(hasLockoutOccurred).isFalse()
+ }
+
private fun setSecurityModeAndDispatchBroadcast(
securityMode: KeyguardSecurityModel.SecurityMode,
) {
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 08cd7ed..10c16bd 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
@@ -19,16 +19,22 @@
import android.app.admin.DevicePolicyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.currentTime
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -43,21 +49,23 @@
private val testScope = utils.testScope
private val underTest = utils.authenticationInteractor()
+ private val onAuthenticationResult by
+ testScope.collectLastValue(underTest.onAuthenticationResult)
+ private val failedAuthenticationAttempts by
+ testScope.collectLastValue(underTest.failedAuthenticationAttempts)
+
@Test
fun authenticationMethod() =
testScope.runTest {
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin)
- assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Pin)
+ assertThat(authMethod).isEqualTo(Pin)
+ assertThat(underTest.getAuthenticationMethod()).isEqualTo(Pin)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
+ utils.authenticationRepository.setAuthenticationMethod(Password)
- assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password)
- assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(AuthenticationMethodModel.Password)
+ assertThat(authMethod).isEqualTo(Password)
+ assertThat(underTest.getAuthenticationMethod()).isEqualTo(Password)
}
@Test
@@ -66,51 +74,45 @@
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.authenticationRepository.setAuthenticationMethod(None)
- assertThat(authMethod).isEqualTo(AuthenticationMethodModel.None)
- assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(AuthenticationMethodModel.None)
+ assertThat(authMethod).isEqualTo(None)
+ assertThat(underTest.getAuthenticationMethod()).isEqualTo(None)
}
@Test
fun authenticate_withCorrectPin_succeeds() =
testScope.runTest {
- val lockout by collectLastValue(underTest.lockout)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
- .isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(lockout).isNull()
- assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
+ assertSucceeded(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
}
@Test
fun authenticate_withIncorrectPin_fails() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
- assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4)))
- .isEqualTo(AuthenticationResult.FAILED)
+ assertFailed(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4)))
}
@Test(expected = IllegalArgumentException::class)
fun authenticate_withEmptyPin_throwsException() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
underTest.authenticate(listOf())
}
@Test
fun authenticate_withCorrectMaxLengthPin_succeeds() =
testScope.runTest {
- val pin = List(16) { 9 }
+ val correctMaxLengthPin = List(16) { 9 }
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
- overrideCredential(pin)
+ setAuthenticationMethod(Pin)
+ overrideCredential(correctMaxLengthPin)
}
- assertThat(underTest.authenticate(pin)).isEqualTo(AuthenticationResult.SUCCEEDED)
+ assertSucceeded(underTest.authenticate(correctMaxLengthPin))
}
@Test
@@ -122,88 +124,64 @@
// If the policy changes, there is work to do in SysUI.
assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- assertThat(underTest.authenticate(List(17) { 9 }))
- .isEqualTo(AuthenticationResult.FAILED)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
+
+ assertFailed(underTest.authenticate(List(17) { 9 }))
}
@Test
fun authenticate_withCorrectPassword_succeeds() =
testScope.runTest {
- val lockout by collectLastValue(underTest.lockout)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
+ utils.authenticationRepository.setAuthenticationMethod(Password)
- assertThat(underTest.authenticate("password".toList()))
- .isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(lockout).isNull()
- assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
+ assertSucceeded(underTest.authenticate("password".toList()))
}
@Test
fun authenticate_withIncorrectPassword_fails() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
+ utils.authenticationRepository.setAuthenticationMethod(Password)
- assertThat(underTest.authenticate("alohomora".toList()))
- .isEqualTo(AuthenticationResult.FAILED)
+ assertFailed(underTest.authenticate("alohomora".toList()))
}
@Test
fun authenticate_withCorrectPattern_succeeds() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Pattern
- )
+ utils.authenticationRepository.setAuthenticationMethod(Pattern)
- assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
- .isEqualTo(AuthenticationResult.SUCCEEDED)
+ assertSucceeded(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
}
@Test
fun authenticate_withIncorrectPattern_fails() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Pattern
- )
-
- assertThat(
- underTest.authenticate(
- listOf(
- AuthenticationPatternCoordinate(x = 2, y = 0),
- AuthenticationPatternCoordinate(x = 2, y = 1),
- AuthenticationPatternCoordinate(x = 2, y = 2),
- AuthenticationPatternCoordinate(x = 1, y = 2),
- )
- )
+ utils.authenticationRepository.setAuthenticationMethod(Pattern)
+ val wrongPattern =
+ listOf(
+ AuthenticationPatternCoordinate(x = 2, y = 0),
+ AuthenticationPatternCoordinate(x = 2, y = 1),
+ AuthenticationPatternCoordinate(x = 2, y = 2),
+ AuthenticationPatternCoordinate(x = 1, y = 2),
)
- .isEqualTo(AuthenticationResult.FAILED)
+
+ assertFailed(underTest.authenticate(wrongPattern))
}
@Test
fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNull() =
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
- val lockout by collectLastValue(underTest.lockout)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(true)
}
assertThat(isAutoConfirmEnabled).isTrue()
+ val shorterPin =
+ FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply { removeLast() }
- assertThat(
- underTest.authenticate(
- FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply {
- removeLast()
- },
- tryAutoConfirm = true
- )
- )
- .isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(lockout).isNull()
+ assertSkipped(underTest.authenticate(shorterPin, tryAutoConfirm = true))
+ assertThat(underTest.lockoutEndTimestamp).isNull()
assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
}
@@ -212,18 +190,17 @@
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(true)
}
assertThat(isAutoConfirmEnabled).isTrue()
- assertThat(
- underTest.authenticate(
- FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 },
- tryAutoConfirm = true
- )
- )
- .isEqualTo(AuthenticationResult.FAILED)
+ val wrongPin = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }
+
+ assertFailed(
+ underTest.authenticate(wrongPin, tryAutoConfirm = true),
+ assertNoResultEvents = true,
+ )
}
@Test
@@ -231,18 +208,17 @@
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(true)
}
assertThat(isAutoConfirmEnabled).isTrue()
- assertThat(
- underTest.authenticate(
- FakeAuthenticationRepository.DEFAULT_PIN + listOf(7),
- tryAutoConfirm = true
- )
- )
- .isEqualTo(AuthenticationResult.FAILED)
+ val longerPin = FakeAuthenticationRepository.DEFAULT_PIN + listOf(7)
+
+ assertFailed(
+ underTest.authenticate(longerPin, tryAutoConfirm = true),
+ assertNoResultEvents = true,
+ )
}
@Test
@@ -250,69 +226,54 @@
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(true)
}
assertThat(isAutoConfirmEnabled).isTrue()
- assertThat(
- underTest.authenticate(
- FakeAuthenticationRepository.DEFAULT_PIN,
- tryAutoConfirm = true
- )
- )
- .isEqualTo(AuthenticationResult.SUCCEEDED)
+ val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
+
+ assertSucceeded(underTest.authenticate(correctPin, tryAutoConfirm = true))
}
@Test
fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringLockout_returnsNull() =
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(true)
- setLockoutDuration(42)
+ reportLockoutStarted(42)
}
- val authResult =
- underTest.authenticate(
- FakeAuthenticationRepository.DEFAULT_PIN,
- tryAutoConfirm = true
- )
+ val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
- assertThat(authResult).isEqualTo(AuthenticationResult.SKIPPED)
+ assertSkipped(underTest.authenticate(correctPin, tryAutoConfirm = true))
assertThat(isAutoConfirmEnabled).isFalse()
- assertThat(isUnlocked).isFalse()
assertThat(hintedPinLength).isNull()
+ assertThat(underTest.lockoutEndTimestamp).isNotNull()
}
@Test
fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNull() =
testScope.runTest {
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(false)
}
- assertThat(
- underTest.authenticate(
- FakeAuthenticationRepository.DEFAULT_PIN,
- tryAutoConfirm = true
- )
- )
- .isEqualTo(AuthenticationResult.SKIPPED)
+
+ val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
+
+ assertSkipped(underTest.authenticate(correctPin, tryAutoConfirm = true))
}
@Test
fun tryAutoConfirm_withoutCorrectPassword_returnsNull() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
+ utils.authenticationRepository.setAuthenticationMethod(Password)
- assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true))
- .isEqualTo(AuthenticationResult.SKIPPED)
+ assertSkipped(underTest.authenticate("password".toList(), tryAutoConfirm = true))
}
@Test
@@ -337,7 +298,6 @@
fun isAutoConfirmEnabled_featureEnabledButDisabledByLockout() =
testScope.runTest {
val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
- val lockout by collectLastValue(underTest.lockout)
utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
// The feature is enabled.
@@ -345,92 +305,153 @@
// Make many wrong attempts to trigger lockout.
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
- underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
+ assertFailed(underTest.authenticate(listOf(5, 6, 7))) // Wrong PIN
}
- assertThat(lockout).isNotNull()
+ assertThat(underTest.lockoutEndTimestamp).isNotNull()
assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
// Lockout disabled auto-confirm.
assertThat(isAutoConfirmEnabled).isFalse()
// Move the clock forward one more second, to completely finish the lockout period:
- advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_MS + 1000L)
- assertThat(lockout).isNull()
+ advanceTimeBy(
+ FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds.plus(1.seconds)
+ )
+ assertThat(underTest.lockoutEndTimestamp).isNull()
// Auto-confirm is still disabled, because lockout occurred at least once in this
// session.
assertThat(isAutoConfirmEnabled).isFalse()
// Correct PIN and unlocks successfully, resetting the 'session'.
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
- .isEqualTo(AuthenticationResult.SUCCEEDED)
+ assertSucceeded(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
// Auto-confirm is re-enabled.
assertThat(isAutoConfirmEnabled).isTrue()
-
- assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
}
@Test
- fun lockout() =
+ fun failedAuthenticationAttempts() =
testScope.runTest {
- val lockout by collectLastValue(underTest.lockout)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
- assertThat(lockout).isNull()
+ val failedAuthenticationAttempts by
+ collectLastValue(underTest.failedAuthenticationAttempts)
+
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
+ val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
+
+ assertSucceeded(underTest.authenticate(correctPin))
+ assertThat(failedAuthenticationAttempts).isEqualTo(0)
+
+ // Make many wrong attempts, leading to lockout:
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { index ->
+ underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
+ assertThat(failedAuthenticationAttempts).isEqualTo(index + 1)
+ }
+
+ // Correct PIN, but locked out, so doesn't attempt it:
+ assertSkipped(underTest.authenticate(correctPin), assertNoResultEvents = false)
+ assertThat(failedAuthenticationAttempts)
+ .isEqualTo(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT)
+
+ // Move the clock forward to finish the lockout period:
+ advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds)
+ assertThat(failedAuthenticationAttempts)
+ .isEqualTo(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT)
+
+ // Correct PIN and no longer locked out so unlocks successfully:
+ assertSucceeded(underTest.authenticate(correctPin))
+ assertThat(failedAuthenticationAttempts).isEqualTo(0)
+ }
+
+ @Test
+ fun lockoutEndTimestamp() =
+ testScope.runTest {
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
+ val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
+
+ underTest.authenticate(correctPin)
+ assertThat(underTest.lockoutEndTimestamp).isNull()
// Make many wrong attempts, but just shy of what's needed to get locked out:
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(lockout).isNull()
+ assertThat(underTest.lockoutEndTimestamp).isNull()
}
// Make one more wrong attempt, leading to lockout:
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(lockout)
- .isEqualTo(
- AuthenticationLockoutModel(
- failedAttemptCount =
- FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
- remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS,
- )
- )
+
+ val expectedLockoutEndTimestamp =
+ testScope.currentTime + FakeAuthenticationRepository.LOCKOUT_DURATION_MS
+ assertThat(underTest.lockoutEndTimestamp).isEqualTo(expectedLockoutEndTimestamp)
assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
// Correct PIN, but locked out, so doesn't attempt it:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
- .isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(lockout)
- .isEqualTo(
- AuthenticationLockoutModel(
- failedAttemptCount =
- FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
- remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS,
- )
- )
+ assertSkipped(underTest.authenticate(correctPin), assertNoResultEvents = false)
+ assertThat(underTest.lockoutEndTimestamp).isEqualTo(expectedLockoutEndTimestamp)
// Move the clock forward to ALMOST skip the lockout, leaving one second to go:
- val lockoutTimeoutSec = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS
- repeat(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS - 1) { time ->
- advanceTimeBy(1000)
- assertThat(lockout)
- .isEqualTo(
- AuthenticationLockoutModel(
- failedAttemptCount =
- FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
- remainingSeconds = lockoutTimeoutSec - (time + 1),
- )
- )
+ repeat(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS - 1) {
+ advanceTimeBy(1.seconds)
+ assertThat(underTest.lockoutEndTimestamp).isEqualTo(expectedLockoutEndTimestamp)
}
// Move the clock forward one more second, to completely finish the lockout period:
- advanceTimeBy(1000)
- assertThat(lockout).isNull()
+ advanceTimeBy(1.seconds)
+ assertThat(underTest.lockoutEndTimestamp).isNull()
// Correct PIN and no longer locked out so unlocks successfully:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
- .isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(lockout).isNull()
+ assertSucceeded(underTest.authenticate(correctPin))
+ assertThat(underTest.lockoutEndTimestamp).isNull()
+ }
+
+ @Test
+ fun upcomingWipe() =
+ testScope.runTest {
+ val upcomingWipe by collectLastValue(underTest.upcomingWipe)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
+ val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
+ val wrongPin = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }
+
+ underTest.authenticate(correctPin)
+ assertThat(upcomingWipe).isNull()
+
+ var expectedFailedAttempts = 0
+ var remainingFailedAttempts =
+ utils.authenticationRepository.getMaxFailedUnlockAttemptsForWipe()
+ assertThat(remainingFailedAttempts)
+ .isGreaterThan(LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE)
+
+ // Make many wrong attempts, until wipe is triggered:
+ repeat(remainingFailedAttempts) { attemptIndex ->
+ underTest.authenticate(wrongPin)
+ expectedFailedAttempts++
+ remainingFailedAttempts--
+ if (underTest.lockoutEndTimestamp != null) {
+ // If there's a lockout, wait it out:
+ advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds)
+ }
+
+ if (attemptIndex < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // No risk of wipe.
+ assertThat(upcomingWipe).isNull()
+ } else {
+ // Wipe grace period started; Make additional wrong attempts, confirm the
+ // warning is shown each time:
+ assertThat(upcomingWipe)
+ .isEqualTo(
+ AuthenticationWipeModel(
+ wipeTarget = AuthenticationWipeModel.WipeTarget.WholeDevice,
+ failedAttempts = expectedFailedAttempts,
+ remainingAttempts = remainingFailedAttempts
+ )
+ )
+ }
+ }
+
+ // Unlock successfully, no more risk of upcoming wipe:
+ assertSucceeded(underTest.authenticate(correctPin))
+ assertThat(upcomingWipe).isNull()
}
@Test
@@ -438,7 +459,7 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(false)
}
@@ -450,7 +471,7 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) }
@@ -467,7 +488,7 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
setAutoConfirmFeatureEnabled(true)
overrideCredential(
buildList {
@@ -484,7 +505,7 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAuthenticationMethod(Pin)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) }
@@ -499,18 +520,45 @@
@Test
fun authenticate_withTooShortPassword() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- assertThat(
- underTest.authenticate(
- buildList {
- repeat(utils.authenticationRepository.minPasswordLength - 1) { time ->
- add("$time")
- }
- }
- )
- )
- .isEqualTo(AuthenticationResult.SKIPPED)
+ utils.authenticationRepository.setAuthenticationMethod(Password)
+
+ val tooShortPassword = buildList {
+ repeat(utils.authenticationRepository.minPasswordLength - 1) { time ->
+ add("$time")
+ }
+ }
+ assertSkipped(underTest.authenticate(tooShortPassword))
}
+
+ private fun assertSucceeded(authenticationResult: AuthenticationResult) {
+ assertThat(authenticationResult).isEqualTo(AuthenticationResult.SUCCEEDED)
+ assertThat(onAuthenticationResult).isTrue()
+ assertThat(underTest.lockoutEndTimestamp).isNull()
+ assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
+ assertThat(failedAuthenticationAttempts).isEqualTo(0)
+ }
+
+ private fun assertFailed(
+ authenticationResult: AuthenticationResult,
+ assertNoResultEvents: Boolean = false,
+ ) {
+ assertThat(authenticationResult).isEqualTo(AuthenticationResult.FAILED)
+ if (assertNoResultEvents) {
+ assertThat(onAuthenticationResult).isNull()
+ } else {
+ assertThat(onAuthenticationResult).isFalse()
+ }
+ }
+
+ private fun assertSkipped(
+ authenticationResult: AuthenticationResult,
+ assertNoResultEvents: Boolean = true,
+ ) {
+ assertThat(authenticationResult).isEqualTo(AuthenticationResult.SKIPPED)
+ if (assertNoResultEvents) {
+ assertThat(onAuthenticationResult).isNull()
+ } else {
+ assertThat(onAuthenticationResult).isNotNull()
+ }
+ }
}
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 da97a12..1c1335f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -923,14 +923,14 @@
doReturn(500).when(mResources)
.getDimensionPixelSize(eq(com.android.systemui.res.R.dimen
.physical_fingerprint_sensor_center_screen_location_y));
- mAuthController.onConfigurationChanged(null /* newConfig */);
+ mAuthController.onConfigChanged(null /* newConfig */);
final Point firstFpLocation = mAuthController.getFingerprintSensorLocation();
doReturn(1000).when(mResources)
.getDimensionPixelSize(eq(com.android.systemui.res.R.dimen
.physical_fingerprint_sensor_center_screen_location_y));
- mAuthController.onConfigurationChanged(null /* newConfig */);
+ mAuthController.onConfigChanged(null /* newConfig */);
assertNotSame(firstFpLocation, mAuthController.getFingerprintSensorLocation());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index a726b7c..b0beab9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -47,6 +47,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
@@ -54,7 +55,6 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
@@ -101,7 +101,6 @@
@Mock
private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
@Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
- @Mock private lateinit var secureSettings: SecureSettings
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
@@ -117,6 +116,7 @@
@Mock
private lateinit var udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -174,6 +174,7 @@
mSelectedUserInteractor,
{ deviceEntryUdfpsTouchOverlayViewModel },
{ defaultUdfpsTouchOverlayViewModel },
+ shadeInteractor
)
block()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index dddcf18..e5da1f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -90,11 +90,11 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -124,8 +124,6 @@
import java.util.ArrayList;
import java.util.List;
-import javax.inject.Provider;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@@ -208,6 +206,8 @@
@Mock
private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock
+ private ShadeInteractor mShadeInteractor;
+ @Mock
private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
@Mock
private SessionTracker mSessionTracker;
@@ -216,8 +216,6 @@
@Mock
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@Mock
- private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
- @Mock
private SelectedUserInteractor mSelectedUserInteractor;
// Capture listeners so that they can be used to send events
@@ -333,13 +331,13 @@
mActivityLaunchAnimator,
mBiometricExecutor,
mPrimaryBouncerInteractor,
+ mShadeInteractor,
mSinglePointerTouchProcessor,
mSessionTracker,
mAlternateBouncerInteractor,
mInputManager,
mock(KeyguardFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
- mUdfpsKeyguardViewModels,
mSelectedUserInteractor,
mFpsUnlockTracker,
mKeyguardTransitionInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index ac16c13..13b53a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -36,6 +36,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -70,6 +71,7 @@
protected @Mock UdfpsController mUdfpsController;
protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ protected @Mock ShadeInteractor mShadeInteractor;
protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
protected @Mock SelectedUserInteractor mSelectedUserInteractor;
@@ -149,7 +151,8 @@
mAlternateBouncerInteractor,
mUdfpsKeyguardAccessibilityDelegate,
mSelectedUserInteractor,
- mKeyguardTransitionInteractor);
+ mKeyguardTransitionInteractor,
+ mShadeInteractor);
return controller;
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 0ab596c..1f8854f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -616,4 +616,28 @@
.onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
job.cancel()
}
+
+ @Test
+ fun bouncerToAod_dozeAmountChanged() =
+ testScope.runTest {
+ // GIVEN view is attached
+ mController.onViewAttached()
+ Mockito.reset(mView)
+
+ val job = mController.listenForPrimaryBouncerToAodTransitions(this)
+ // WHEN alternate bouncer to aod transition in progress
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.AOD,
+ value = .3f,
+ transitionState = TransitionState.RUNNING
+ )
+ )
+ runCurrent()
+
+ // THEN doze amount is updated to
+ verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF))
+ job.cancel()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 9b1df7c..99c1874 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -21,15 +21,15 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
-import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -79,7 +79,7 @@
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
underTest.clearMessage()
- assertThat(message).isEmpty()
+ assertThat(message).isNull()
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
@@ -149,7 +149,7 @@
// Incomplete input.
assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(message).isEmpty()
+ assertThat(message).isNull()
// Correct input.
assertThat(
@@ -159,7 +159,7 @@
)
)
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(message).isEmpty()
+ assertThat(message).isNull()
}
@Test
@@ -246,57 +246,40 @@
}
@Test
- fun lockout() =
+ fun lockoutStarted() =
testScope.runTest {
- val lockout by collectLastValue(underTest.lockout)
+ val lockoutStartedEvents by collectValues(underTest.onLockoutStarted)
val message by collectLastValue(underTest.message)
+
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- assertThat(lockout).isNull()
+ assertThat(lockoutStartedEvents).isEmpty()
+
+ // Try the wrong PIN repeatedly, until lockout is triggered:
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times ->
// Wrong PIN.
assertThat(underTest.authenticate(listOf(6, 7, 8, 9)))
.isEqualTo(AuthenticationResult.FAILED)
if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
- assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
+ assertThat(lockoutStartedEvents).isEmpty()
+ assertThat(message).isNotEmpty()
}
}
- assertThat(lockout)
- .isEqualTo(
- AuthenticationLockoutModel(
- failedAttemptCount =
- FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT,
- remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS,
- )
- )
- assertTryAgainMessage(
- message,
- FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt()
- )
+ assertThat(authenticationInteractor.lockoutEndTimestamp).isNotNull()
+ assertThat(lockoutStartedEvents.size).isEqualTo(1)
+ assertThat(message).isNull()
- // Correct PIN, but locked out, so doesn't change away from the bouncer scene:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
- .isEqualTo(AuthenticationResult.SKIPPED)
- assertTryAgainMessage(
- message,
- FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt()
- )
+ // Advance the time to finish the lockout:
+ advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS.seconds)
+ assertThat(authenticationInteractor.lockoutEndTimestamp).isNull()
+ assertThat(message).isNull()
+ assertThat(lockoutStartedEvents.size).isEqualTo(1)
- lockout?.remainingSeconds?.let { seconds ->
- repeat(seconds) { time ->
- advanceTimeBy(1000)
- val remainingTimeSec = seconds - time - 1
- if (remainingTimeSec > 0) {
- assertTryAgainMessage(message, remainingTimeSec)
- }
- }
+ // Trigger lockout again:
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
+ // Wrong PIN.
+ underTest.authenticate(listOf(6, 7, 8, 9))
}
- assertThat(message).isEqualTo("")
- assertThat(lockout).isNull()
-
- // Correct PIN and no longer locked out so changes to the Gone scene:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
- .isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(lockout).isNull()
+ assertThat(lockoutStartedEvents.size).isEqualTo(2)
}
@Test
@@ -326,13 +309,6 @@
verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput()
}
- private fun assertTryAgainMessage(
- message: String?,
- time: Int,
- ) {
- assertThat(message).isEqualTo("Try again in $time seconds.")
- }
-
companion object {
private const val MESSAGE_ENTER_YOUR_PIN = "Enter your PIN"
private const val MESSAGE_ENTER_YOUR_PASSWORD = "Enter your password"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 16a9359..47bbe6f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -21,6 +21,11 @@
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.authentication.shared.model.AuthenticationMethodModel.None
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
@@ -33,6 +38,7 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.currentTime
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -46,7 +52,6 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
- private val actionButtonInteractor = utils.bouncerActionButtonInteractor()
private val bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
@@ -55,7 +60,6 @@
utils.bouncerViewModel(
bouncerInteractor = bouncerInteractor,
authenticationInteractor = authenticationInteractor,
- actionButtonInteractor = actionButtonInteractor,
)
@Test
@@ -135,19 +139,47 @@
fun message() =
testScope.runTest {
val message by collectLastValue(underTest.message)
- val lockout by collectLastValue(bouncerInteractor.lockout)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
assertThat(message?.isUpdateAnimated).isTrue()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
- // Wrong PIN.
- bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
+ bouncerInteractor.authenticate(WRONG_PIN)
}
assertThat(message?.isUpdateAnimated).isFalse()
- lockout?.remainingSeconds?.let { remainingSeconds ->
- advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
+ val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0
+ advanceTimeBy(lockoutEndMs - testScope.currentTime)
+ assertThat(message?.isUpdateAnimated).isTrue()
+ }
+
+ @Test
+ fun lockoutMessage() =
+ testScope.runTest {
+ val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
+ val message by collectLastValue(underTest.message)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
+ assertThat(utils.authenticationRepository.lockoutEndTimestamp).isNull()
+ assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()
+
+ repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times ->
+ bouncerInteractor.authenticate(WRONG_PIN)
+ if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
+ assertThat(message?.text).isEqualTo(bouncerInteractor.message.value)
+ assertThat(message?.isUpdateAnimated).isTrue()
+ }
}
+ val lockoutSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS
+ assertTryAgainMessage(message?.text, lockoutSeconds)
+ assertThat(message?.isUpdateAnimated).isFalse()
+
+ repeat(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS) { time ->
+ advanceTimeBy(1.seconds)
+ val remainingSeconds = lockoutSeconds - time - 1
+ if (remainingSeconds > 0) {
+ assertTryAgainMessage(message?.text, remainingSeconds)
+ }
+ }
+ assertThat(message?.text).isEmpty()
assertThat(message?.isUpdateAnimated).isTrue()
}
@@ -160,37 +192,36 @@
authViewModel?.isInputEnabled ?: emptyFlow()
}
)
- val lockout by collectLastValue(bouncerInteractor.lockout)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
assertThat(isInputEnabled).isTrue()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
- // Wrong PIN.
- bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
+ bouncerInteractor.authenticate(WRONG_PIN)
}
assertThat(isInputEnabled).isFalse()
- lockout?.remainingSeconds?.let { remainingSeconds ->
- advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds)
- }
+ val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0
+ advanceTimeBy(lockoutEndMs - testScope.currentTime)
assertThat(isInputEnabled).isTrue()
}
@Test
- fun dialogMessage() =
+ fun dialogViewModel() =
testScope.runTest {
- val dialogMessage by collectLastValue(underTest.dialogMessage)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
+ val dialogViewModel by collectLastValue(underTest.dialogViewModel)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
+ assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
- // Wrong PIN.
- assertThat(dialogMessage).isNull()
- bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
+ assertThat(dialogViewModel).isNull()
+ bouncerInteractor.authenticate(WRONG_PIN)
}
- assertThat(dialogMessage).isNotEmpty()
+ assertThat(dialogViewModel).isNotNull()
+ assertThat(dialogViewModel?.text).isNotEmpty()
- underTest.onDialogDismissed()
- assertThat(dialogMessage).isNull()
+ dialogViewModel?.onDismiss?.invoke()
+ assertThat(dialogViewModel).isNull()
}
@Test
@@ -198,20 +229,16 @@
testScope.runTest {
val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
assertThat(isSideBySideSupported).isTrue()
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
+ utils.authenticationRepository.setAuthenticationMethod(Password)
assertThat(isSideBySideSupported).isTrue()
utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
assertThat(isSideBySideSupported).isTrue()
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
+ utils.authenticationRepository.setAuthenticationMethod(Password)
assertThat(isSideBySideSupported).isFalse()
}
@@ -219,26 +246,27 @@
fun isFoldSplitRequired() =
testScope.runTest {
val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setAuthenticationMethod(Pin)
assertThat(isFoldSplitRequired).isTrue()
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
+ utils.authenticationRepository.setAuthenticationMethod(Password)
assertThat(isFoldSplitRequired).isFalse()
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Pattern
- )
+ utils.authenticationRepository.setAuthenticationMethod(Pattern)
assertThat(isFoldSplitRequired).isTrue()
}
private fun authMethodsToTest(): List<AuthenticationMethodModel> {
- return listOf(
- AuthenticationMethodModel.None,
- AuthenticationMethodModel.Pin,
- AuthenticationMethodModel.Password,
- AuthenticationMethodModel.Pattern,
- AuthenticationMethodModel.Sim,
- )
+ return listOf(None, Pin, Password, Pattern, Sim)
+ }
+
+ private fun assertTryAgainMessage(
+ message: String?,
+ time: Int,
+ ) {
+ assertThat(message).isEqualTo("Try again in $time seconds.")
+ }
+
+ companion object {
+ private val WRONG_PIN = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 6d6baa5..64e6e57 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -28,6 +27,7 @@
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -45,11 +45,7 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationRepository = utils.authenticationRepository
- private val authenticationInteractor =
- utils.authenticationInteractor(
- repository = authenticationRepository,
- )
+ private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
private val bouncerInteractor =
utils.bouncerInteractor(
@@ -61,12 +57,13 @@
authenticationInteractor = authenticationInteractor,
actionButtonInteractor = utils.bouncerActionButtonInteractor(),
)
+ private val isInputEnabled = MutableStateFlow(true)
private val underTest =
PasswordBouncerViewModel(
viewModelScope = testScope.backgroundScope,
interactor = bouncerInteractor,
- isInputEnabled = MutableStateFlow(true).asStateFlow(),
+ isInputEnabled.asStateFlow(),
)
@Before
@@ -123,8 +120,7 @@
@Test
fun onAuthenticateKeyPressed_whenCorrect() =
testScope.runTest {
- val authResult by
- collectLastValue(authenticationInteractor.authenticationChallengeResult)
+ val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
lockDeviceAndOpenPasswordBouncer()
underTest.onPasswordInputChanged("password")
@@ -169,8 +165,7 @@
@Test
fun onAuthenticateKeyPressed_correctAfterWrong() =
testScope.runTest {
- val authResult by
- collectLastValue(authenticationInteractor.authenticationChallengeResult)
+ val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
lockDeviceAndOpenPasswordBouncer()
@@ -333,19 +328,15 @@
) {
if (isLockedOut) {
repeat(failedAttemptCount) {
- authenticationRepository.reportAuthenticationAttempt(false)
+ utils.authenticationRepository.reportAuthenticationAttempt(false)
}
- val remainingTimeSeconds = 30
- authenticationRepository.setLockoutDuration(remainingTimeSeconds * 1000)
- authenticationRepository.lockout.value =
- AuthenticationLockoutModel(
- failedAttemptCount = failedAttemptCount,
- remainingSeconds = remainingTimeSeconds,
- )
+ utils.authenticationRepository.reportLockoutStarted(
+ 30.seconds.inWholeMilliseconds.toInt()
+ )
} else {
- authenticationRepository.reportAuthenticationAttempt(true)
- authenticationRepository.lockout.value = null
+ utils.authenticationRepository.reportAuthenticationAttempt(true)
}
+ isInputEnabled.value = !isLockedOut
runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 8971423..83d1938 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -111,8 +111,7 @@
@Test
fun onDragEnd_whenCorrect() =
testScope.runTest {
- val authResult by
- collectLastValue(authenticationInteractor.authenticationChallengeResult)
+ val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
lockDeviceAndOpenPatternBouncer()
@@ -304,7 +303,7 @@
fun onDragEnd_whenPatternTooShort() =
testScope.runTest {
val message by collectLastValue(bouncerViewModel.message)
- val dialogMessage by collectLastValue(bouncerViewModel.dialogMessage)
+ val dialogViewModel by collectLastValue(bouncerViewModel.dialogViewModel)
lockDeviceAndOpenPatternBouncer()
// Enter a pattern that's too short more than enough times that would normally trigger
@@ -327,15 +326,14 @@
underTest.onDragEnd()
assertWithMessage("Attempt #$attempt").that(message?.text).isEqualTo(WRONG_PATTERN)
- assertWithMessage("Attempt #$attempt").that(dialogMessage).isNull()
+ assertWithMessage("Attempt #$attempt").that(dialogViewModel).isNull()
}
}
@Test
fun onDragEnd_correctAfterWrong() =
testScope.runTest {
- val authResult by
- collectLastValue(authenticationInteractor.authenticationChallengeResult)
+ val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index c30e405..db98d76 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -201,8 +201,7 @@
@Test
fun onAuthenticateButtonClicked_whenCorrect() =
testScope.runTest {
- val authResult by
- collectLastValue(authenticationInteractor.authenticationChallengeResult)
+ val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
lockDeviceAndOpenPinBouncer()
FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
@@ -236,8 +235,7 @@
@Test
fun onAuthenticateButtonClicked_correctAfterWrong() =
testScope.runTest {
- val authResult by
- collectLastValue(authenticationInteractor.authenticationChallengeResult)
+ val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
lockDeviceAndOpenPinBouncer()
@@ -265,8 +263,7 @@
fun onAutoConfirm_whenCorrect() =
testScope.runTest {
utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
- val authResult by
- collectLastValue(authenticationInteractor.authenticationChallengeResult)
+ val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
lockDeviceAndOpenPinBouncer()
FakeAuthenticationRepository.DEFAULT_PIN.forEach(underTest::onPinButtonClicked)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 182712a..ddaa488 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -208,11 +208,11 @@
val repository = initCommunalWidgetRepository()
runCurrent()
- val ids = listOf(104, 103, 101)
- repository.updateWidgetOrder(ids)
+ val widgetIdToPriorityMap = mapOf(104 to 1, 103 to 2, 101 to 3)
+ repository.updateWidgetOrder(widgetIdToPriorityMap)
runCurrent()
- verify(communalWidgetDao).updateWidgetOrder(ids)
+ verify(communalWidgetDao).updateWidgetOrder(widgetIdToPriorityMap)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index d3049d9..565049b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -99,7 +99,7 @@
}
@Test
- fun reportSuccessfulAuthentication_shouldUpdateIsUnlocked() =
+ fun reportSuccessfulAuthentication_updatesIsUnlocked() =
testScope.runTest {
val isUnlocked by collectLastValue(underTest.isUnlocked)
assertThat(isUnlocked).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 910097e..ea19cb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -19,6 +19,7 @@
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.coroutines.collectValues
@@ -346,12 +347,14 @@
}
@Test
- fun successfulAuthenticationChallengeAttempt_updatedIsUnlockedState() =
+ fun successfulAuthenticationChallengeAttempt_updatesIsUnlockedState() =
testScope.runTest {
val isUnlocked by collectLastValue(underTest.isUnlocked)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
assertThat(isUnlocked).isFalse()
- utils.authenticationRepository.reportAuthenticationAttempt(true)
+ authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
assertThat(isUnlocked).isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index d6d5b23..941d67f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -56,7 +56,6 @@
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.display.data.repository.display
import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -73,6 +72,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
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.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 92c2d74..4a39ba2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -21,9 +21,9 @@
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.dump.LogcatEchoTrackerAlways
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.LogcatEchoTrackerAlways
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.viewmodel.QSTileState
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 c110de9..224903f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -265,8 +265,8 @@
falsingCollector = utils.falsingCollector(),
powerInteractor = powerInteractor,
bouncerInteractor = bouncerInteractor,
- simBouncerInteractor = utils.simBouncerInteractor,
- authenticationInteractor = utils.authenticationInteractor()
+ simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
+ authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() }
)
startable.start()
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 61d55f0..2e4986d 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
@@ -90,8 +90,8 @@
falsingCollector = falsingCollector,
powerInteractor = powerInteractor,
bouncerInteractor = bouncerInteractor,
- simBouncerInteractor = utils.simBouncerInteractor,
- authenticationInteractor = authenticationInteractor,
+ simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
+ authenticationInteractor = dagger.Lazy { authenticationInteractor },
)
@Test
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 2ecf01f..1237347 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
@@ -16,7 +16,8 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
-import android.platform.test.flag.junit.SetFlagsRule
+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.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
@@ -60,8 +61,6 @@
private lateinit var underTest: WifiViewModel
- private val setFlagsRule = SetFlagsRule()
-
@Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
@@ -187,11 +186,9 @@
}
@Test
+ @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
fun activity_nullSsid_outputsFalse_staticFlagOff() =
testScope.runTest {
- // GIVEN flag is disabled
- setFlagsRule.disableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
-
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
@@ -214,11 +211,9 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
fun activity_nullSsid_outputsFalse_staticFlagOn() =
testScope.runTest {
- // GIVEN flag is enabled
- setFlagsRule.enableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
-
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
@@ -371,11 +366,9 @@
}
@Test
+ @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
fun activityContainer_inAndOutFalse_outputsTrue_staticFlagOff() =
testScope.runTest {
- // GIVEN the flag is off
- setFlagsRule.disableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
-
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
@@ -389,11 +382,9 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
fun activityContainer_inAndOutFalse_outputsTrue_staticFlagOn() =
testScope.runTest {
- // GIVEN the flag is on
- setFlagsRule.enableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
-
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 1c5f221..4436be7 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -41,16 +41,13 @@
/** Interface for building clocks and providing information about those clocks */
interface ClockProvider {
+ /** Initializes the clock provider with debug log buffers */
+ fun initialize(buffers: ClockMessageBuffers?)
+
/** Returns metadata for all clocks this provider knows about */
fun getClocks(): List<ClockMetadata>
/** Initializes and returns the target clock design */
- @Deprecated("Use overload with ClockSettings")
- fun createClock(id: ClockId): ClockController {
- return createClock(ClockSettings(id, null))
- }
-
- /** Initializes and returns the target clock design */
fun createClock(settings: ClockSettings): ClockController
/** A static thumbnail for rendering in some examples */
@@ -98,11 +95,20 @@
/** Triggers for various animations */
val animations: ClockAnimations
-
- /** Some clocks may log debug information */
- var messageBuffer: MessageBuffer?
}
+/** For clocks that want to report debug information */
+data class ClockMessageBuffers(
+ /** Message buffer for general infra */
+ val infraMessageBuffer: MessageBuffer,
+
+ /** Message buffer for small clock renering */
+ val smallClockMessageBuffer: MessageBuffer,
+
+ /** Message buffer for large clock rendering */
+ val largeClockMessageBuffer: MessageBuffer,
+)
+
/** Specifies layout information for the */
interface ClockFaceLayout {
/** All clock views to add to the root constraint layout before applying constraints. */
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 557fbf2..e6122a0 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -43,7 +43,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
- android:id="@+id/status_view_media_container"
+ android:id="@id/status_view_media_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/qs_media_padding"
diff --git a/packages/SystemUI/res/drawable/connected_display_dialog_bg.xml b/packages/SystemUI/res/drawable/connected_display_dialog_bg.xml
new file mode 100644
index 0000000..2dce37d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/connected_display_dialog_bg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:topLeftRadius="28dp"
+ android:topRightRadius="28dp"/>
+ <solid android:color="?android:attr/colorBackground" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_memory.xml b/packages/SystemUI/res/drawable/ic_memory.xml
deleted file mode 100644
index ada36c5..0000000
--- a/packages/SystemUI/res/drawable/ic_memory.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M16.0,5.0l-8.0,0.0l0.0,14.0l8.0,0.0z"
- android:fillAlpha="0.5"
- android:fillColor="#000000"/>
- <path
- android:pathData="M6,9 L6,7 L4,7 L4,5 L6,5 C6,3.9 6.9,3 8,3 L16,3 C17.1,3 18,3.9 18,5 L20,5 L20,7 L18,7 L18,9 L20,9 L20,11 L18,11 L18,13 L20,13 L20,15 L18,15 L18,17 L20,17 L20,19 L18,19 C18,20.1 17.1,21 16,21 L8,21 C6.9,21 6,20.1 6,19 L4,19 L4,17 L6,17 L6,15 L4,15 L4,13 L6,13 L6,11 L4,11 L4,9 L6,9 Z M16,19 L16,5 L8,5 L8,19 L16,19 Z"
- android:fillColor="#000000"/>
-</vector>
diff --git a/packages/SystemUI/res/layout/connected_display_dialog.xml b/packages/SystemUI/res/layout/connected_display_dialog.xml
index 8d7f7eb..a71782b 100644
--- a/packages/SystemUI/res/layout/connected_display_dialog.xml
+++ b/packages/SystemUI/res/layout/connected_display_dialog.xml
@@ -22,7 +22,7 @@
android:orientation="vertical"
android:paddingHorizontal="@dimen/dialog_side_padding"
android:paddingTop="@dimen/dialog_top_padding"
- android:background="@*android:drawable/bottomsheet_background"
+ android:background="@drawable/connected_display_dialog_bg"
android:paddingBottom="@dimen/dialog_bottom_padding">
<ImageView
@@ -40,7 +40,7 @@
android:id="@+id/connected_display_dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/screenrecord_title_margin_top"
+ android:layout_marginTop="16dp"
android:gravity="center"
android:text="@string/connected_display_dialog_start_mirroring"
android:textAppearance="@style/TextAppearance.Dialog.Title" />
@@ -51,13 +51,14 @@
android:layout_height="wrap_content"
android:gravity="center"
android:visibility="gone"
+ android:layout_marginTop="16dp"
android:text="@string/connected_display_dialog_dual_display_stop_warning"
android:textAppearance="@style/TextAppearance.Dialog.Body" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/screenrecord_buttons_margin_top"
+ android:layout_marginTop="16dp"
android:orientation="horizontal">
<Button
diff --git a/packages/SystemUI/res/layout/widget_picker.xml b/packages/SystemUI/res/layout/widget_picker.xml
deleted file mode 100644
index 21dc224..0000000
--- a/packages/SystemUI/res/layout/widget_picker.xml
+++ /dev/null
@@ -1,30 +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.
- -->
-
-<HorizontalScrollView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id="@+id/widgets_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:orientation="horizontal">
- </LinearLayout>
-
-</HorizontalScrollView>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index fba6d21..db4cca3 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skermopname"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Begin"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Neem kwessie op"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Begin"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Werkverrigting"</item>
+ <item msgid="1627504621139124393">"Gebruikerkoppelvlak"</item>
+ <item msgid="8309220355268900335">"Battery"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontras"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standaard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swiep links om die gemeenskaplike tutoriaal te begin"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Maak die legstukredigeerder oop"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Verwyder"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Voeg legstuk by"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Klaar"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tik om Batterybespaarder te skeduleer"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Skakel aan wanneer battery waarskynlik sal leegloop"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nee, dankie"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Stort SysUI-hoop"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In gebruik"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programme gebruik tans jou <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stel versteknotasapp in Instellings"</string>
<string name="install_app" msgid="5066668100199613936">"Installeer app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Sinkroniseer wedersyds na eksterne skerm?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Enige Dual Screen-aktiwiteit wat tans loop sal gestop word"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Sinkroniseer skerm wedersyds"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Maak toe"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Skerm is gekoppel"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 12276f5..74e92ba 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"አፈጻጸም"</item>
+ <item msgid="1627504621139124393">"የተጠቃሚ በይነገፅ"</item>
+ <item msgid="8309220355268900335">"ባትሪ"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"ንጽጽር"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"መደበኛ"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"የጋራ አጋዥ ሥልጠናውን ለመጀመር ወደ ግራ ያንሸራትቱ።"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"የምግብር አርታዒውን ይክፈቱ"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"አስወግድ"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ምግብር አክል"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ተከናውኗል"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ለባትሪ ቆጣቢ መርሐግብርን ለማስያዝ መታ ያድርጉ"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ባትሪው የማለቅ ዕድሉ ከፍ ያለ ከሆነ ያብሩት"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"አይ፣ አመሰግናለሁ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI Heap አራግፍ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"በጥቅም ላይ"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"መተግበሪያዎች የእርስዎን <xliff:g id="TYPES_LIST">%s</xliff:g> እየተጠቀሙ ነው።"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"፣ "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"በቅንብሮች ውስጥ ነባሪ የማስታወሻዎች መተግበሪያን ያቀናብሩ"</string>
<string name="install_app" msgid="5066668100199613936">"መተግበሪያን ጫን"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ወደ ውጫዊ ማሳያ ይንጸባረቅ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"በአሁኑ ጊዜ እያሄደ ያለው ማንኛውም የDual Screen እንቅስቃሴ ይቆማል"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ማሳያን አንጸባርቅ"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"አሰናብት"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ማሳያ ተገናኝቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ac76ed0..8aeeaf7 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -336,6 +336,14 @@
<skip />
<!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
<skip />
+ <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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"الأداء"</item>
+ <item msgid="1627504621139124393">"واجهة المستخدم"</item>
+ <item msgid="8309220355268900335">"البطارية"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"التباين"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"عادي"</string>
@@ -465,7 +473,7 @@
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"محو جميع الإشعارات الصامتة"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"تم إيقاف الإشعارات مؤقتًا وفقًا لإعداد \"عدم الإزعاج\""</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"البدء الآن"</string>
- <string name="empty_shade_text" msgid="8935967157319717412">"ليس هناك أي اشعارات"</string>
+ <string name="empty_shade_text" msgid="8935967157319717412">"ما مِن إشعارات."</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ما مِن إشعارات جديدة"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"افتَح قفل الشاشة لعرض الإشعارات الأقدم."</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"يتولّى أحد الوالدين إدارة هذا الجهاز."</string>
@@ -867,7 +875,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"انقر لجدولة \"توفير شحن البطارية\"."</string>
<string name="auto_saver_text" msgid="3214960308353838764">"فعِّل الميزة إذا كان من المرجح نفاد شحن البطارية."</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"لا، شكرًا"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"تفريغ ذاكرة SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"قيد الاستخدام"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"تستخدم التطبيقات <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
@@ -1207,7 +1214,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"يمكنك ضبط تطبيق تدوين الملاحظات التلقائي في \"الإعدادات\"."</string>
<string name="install_app" msgid="5066668100199613936">"تثبيت التطبيق"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"هل تريد بث محتوى جهازك على الشاشة الخارجية؟"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"بث المحتوى على الشاشة"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"إغلاق"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 21da387..a1b5389 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"কাৰ্যদক্ষতা"</item>
+ <item msgid="1627504621139124393">"ব্যৱহাৰকাৰীৰ ইণ্টাৰফে’চ"</item>
+ <item msgid="8309220355268900335">"বেটাৰী"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"কনট্ৰাষ্ট"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"মানক"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"সম্প্ৰদায় সম্পৰ্কীয় নিৰ্দেশনা আৰম্ভ কৰিবলৈ বাওঁফালে ছোৱাইপ কৰক"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ৱিজেট সম্পাদকটো খোলক"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"আঁতৰাওক"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ৱিজেট যোগ দিয়ক"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"কৰা হ’ল"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"বেটাৰী সঞ্চয়কাৰীৰ সময়সূচী সক্ৰিয় কৰিবলৈ টিপক"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"বেটাৰী শেষ হোৱাৰ সম্ভাৱনা থাকিলে অন কৰক"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"নালাগে, ধন্যবাদ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI হীপ ডাম্প কৰক"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ব্যৱহাৰ হৈ আছে"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"এপ্লিকেশ্বনসমূহে আপোনাৰ <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যৱহাৰ কৰি আছে।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ছেটিঙত টোকাৰ ডিফ’ল্ট এপ্ ছেট কৰক"</string>
<string name="install_app" msgid="5066668100199613936">"এপ্টো ইনষ্টল কৰক"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"বাহ্যিক ডিছপ্লে’লৈ মিৰ’ৰ কৰিবনে?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"বৰ্তমান চলি থকা যিকোনো দ্বৈত স্ক্ৰীনৰ কাৰ্যকলাপ বন্ধ কৰা হ’ব"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ডিছপ্লে’ মিৰ’ৰ কৰক"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"অগ্ৰাহ্য কৰক"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ডিছপ্লে’ সংযোগ কৰা হৈছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index ec4d712..2a9a9cf 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yazması"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlayın"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dayandırın"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Qeyd problemi"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Başlayın"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dayandırın"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performans"</item>
+ <item msgid="1627504621139124393">"İstifadəçi interfeysi"</item>
+ <item msgid="8309220355268900335">"Batareya"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standart"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"İcma təlimatını başlatmaq üçün sola sürüşdürün"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vidcet redaktorunu açın"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Silin"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidcet əlavə edin"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hazırdır"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Batareya Qənaətini planlaşdırmaq üçün klikləyin"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Batareya bitmək üzrə olduqda aktiv edin"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Xeyr, təşəkkür"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"İstifadə olunur"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Tətbiqlər <xliff:g id="TYPES_LIST">%s</xliff:g> istifadə edir."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlarda defolt qeydlər tətbiqi ayarlayın"</string>
<string name="install_app" msgid="5066668100199613936">"Tətbiqi quraşdırın"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Xarici displeyə əks etdirilsin?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Davam edən istənilən dual screen fəaliyyəti dayandırılacaq"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Displeyi əks etdirin"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"İmtina edin"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Displey qoşulub"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 1320d0a..47468ff 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Počnite"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Evidentirajte problem"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji deo doživljaja na uređaju je ovo uticalo?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izaberite tip problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Učinak"</item>
+ <item msgid="1627504621139124393">"Korisnički interfejs"</item>
+ <item msgid="8309220355268900335">"Baterija"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardno"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulevo da biste započeli zajednički vodič"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otvori uređivač vidžeta"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj vidžet"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Dodirnite da biste napravili raspored za uštedu baterije"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Uključite ako će baterija verovatno da se isprazni"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izdvoji SysUI mem."</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Podesite podrazumevanu aplikaciju za beleške u Podešavanjima"</string>
<string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li da preslikate na spoljnji ekran?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Biće zaustavljena svaka aktivnost dvojnog ekrana koja je u toku"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran je povezan"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 570acc8..3bc6269 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Прадукцыйнасць"</item>
+ <item msgid="1627504621139124393">"Карыстальніцкі інтэрфейс"</item>
+ <item msgid="8309220355268900335">"Акумулятар"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Кантрастнасць"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартная"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Правядзіце пальцам па экране ўлева, каб азнаёміцца з дапаможнікам"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Адкрыць рэдактар віджэтаў"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Выдаліць"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Дадаць віджэт"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Гатова"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Націсніце, каб уключыць рэжым эканоміі зараду"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Уключыце, калі зарад акумулятара заканчваецца"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, дзякуй"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Выкарыстоўваецца"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Праграмы выкарыстоўваюць: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайце ў Наладах стандартную праграму для нататак"</string>
<string name="install_app" msgid="5066668100199613936">"Усталяваць праграму"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Адлюстраваць на знешнім дысплэі?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Функцыя адначасовага выкарыстання двух экранаў будзе спынена, калі яна актыўная"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Адлюстраваць дысплэй"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Закрыць"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Дысплэй падключаны"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index e52b793..1fe96a6 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Ефективност"</item>
+ <item msgid="1627504621139124393">"Потребителски интерфейс"</item>
+ <item msgid="8309220355268900335">"Батерия"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Контраст"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартен"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Прекарайте пръст наляво, за да стартирате общия урок"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Отваряне на редактора на приспособлението"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Премахване"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Добавяне на приспособление"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Готово"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Докоснете, за да активирате автоматичния режим за запазване на батерията"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Включване, когато е вероятно батерията да се изтощи"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, благодаря"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Използва се"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Някои приложения използват <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартно приложение за бележки от настройките"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталиране на приложението"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се дублира ли на външния екран?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Всяка текуща активност с функцията Dual Screen ще бъде спряна"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Дублиране на дисплея"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Отхвърляне"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Свързан е екран"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5e65830..fa30131 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"পারফর্ম্যান্স"</item>
+ <item msgid="1627504621139124393">"ইউজার ইন্টারফেস"</item>
+ <item msgid="8309220355268900335">"ব্যাটারি"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"কনট্রাস্ট"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"স্ট্যান্ডার্ড"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চার্জ হচ্ছে • পুরো চার্জ হতে আরও <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> সময় লাগবে"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"কমিউনিটি টিউটোরিয়াল চালু করতে বাঁদিকে সোয়াইপ করুন"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"উইজেট এডিটর খুলুন"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"সরান"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"উইজেট যোগ করুন"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"হয়ে গেছে"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ব্যাটারি সেভার চালু হওয়ার সময় সেট করতে ট্যাপ করুন"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ব্যাটারির চার্জ শেষ হয়ে যাওয়ার সম্ভাবনা দেখা দিলে চালু করুন"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"না থাক"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ব্যবহার হচ্ছে"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"অ্যাপ্লিকেশনগুলি আপনার <xliff:g id="TYPES_LIST">%s</xliff:g> ব্যবহার করছে।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"\'সেটিংস\' থেকে ডিফল্ট নোট নেওয়ার অ্যাপ সেট করুন"</string>
<string name="install_app" msgid="5066668100199613936">"অ্যাপ ইনস্টল করুন"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"এক্সটার্নাল ডিসপ্লেতে মিরর করবেন?"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"ডিসপ্লে দেখান"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"বাতিল করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f320d40..db5987fb 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Započnite"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performanse"</item>
+ <item msgid="1627504621139124393">"Korisnički interfejs"</item>
+ <item msgid="8309220355268900335">"Baterija"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardno"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulijevo da pokrenete zajednički vodič"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otvaranje uređivača vidžeta"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Uklanjanje"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajte vidžet"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Dodirnite da zakažete Uštedu baterije"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Uključite ako je vjerovatno da će se baterija istrošiti"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izdvoji SysUI mem."</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije koriste <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u Postavkama"</string>
<string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Preslikati na vanjski ekran?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Bilo koja trenutno pokrenuta aktivnost na dvostrukom ekranu se zaustaviti"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran je povezan"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a8e908f..af4492b 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravació de pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inicia"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Atura"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra el problema"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Inicia"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Atura"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Rendiment"</item>
+ <item msgid="1627504621139124393">"Interfície d\'usuari"</item>
+ <item msgid="8309220355268900335">"Bateria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estàndard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Llisca cap a l\'esquerra per iniciar el tutorial de la comunitat"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Obre l\'editor de widgets"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Suprimeix"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Afegeix un widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Fet"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Toca per programar l\'estalvi de bateria"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Activa\'l quan sigui probable que et quedis sense bateria"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, gràcies"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Aboca el monticle de SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En ús"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Algunes aplicacions estan fent servir el següent: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defineix l\'aplicació de notes predeterminada a Configuració"</string>
<string name="install_app" msgid="5066668100199613936">"Instal·la l\'aplicació"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Replicar a la pantalla externa?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualsevol activitat de pantalla dual que s\'estigui executant en aquests moments s\'aturarà"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Replica la pantalla"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Ignora"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla connectada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 79aa029..25fcf99 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Spustit"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončit"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenat problém"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Spustit"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ukončit"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Výkon"</item>
+ <item msgid="1627504621139124393">"Uživatelské rozhraní"</item>
+ <item msgid="8309220355268900335">"Baterie"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardní"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Přejetím doleva spustíte komunitní výukový program"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otevřít editor widgetů"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstranit"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Přidat widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hotovo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Klepnutím naplánujete aktivování spořiče baterie"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Zapnout, když bude pravděpodobné, že se vybije baterie"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, díky"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Výpis haldy SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Používá se"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikace využívají tato oprávnění: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Výchozí aplikaci pro poznámky nastavíte v Nastavení"</string>
<string name="install_app" msgid="5066668100199613936">"Nainstalovat aplikaci"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Běžící aktivita Dual Screen bude ukončena"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Zavřít"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Displej připojen"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index b418ce8..91c223a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -200,7 +200,7 @@
<string name="accessibility_bluetooth_device_settings_see_all" msgid="9111952496905423543">"Klik for at se alle enheder"</string>
<string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="2435184865793496966">"Klik for at parre en ny enhed"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Batteriniveauet er ukendt."</string>
- <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tilsluttet <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
+ <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Forbundet med <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"Forbundet til <xliff:g id="CAST">%s</xliff:g>."</string>
<string name="accessibility_not_connected" msgid="4061305616351042142">"Ikke tilsluttet."</string>
<string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -293,8 +293,8 @@
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrer brugere"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Udfør"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Luk"</string>
- <string name="quick_settings_connected" msgid="3873605509184830379">"Tilsluttet"</string>
- <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Tilsluttet – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="quick_settings_connected" msgid="3873605509184830379">"Forbundet"</string>
+ <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Forbundet – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="quick_settings_connecting" msgid="2381969772953268809">"Opretter forbindelse…"</string>
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Aktiverer…"</string>
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skærmoptagelse"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Optag problem"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Ydeevne"</item>
+ <item msgid="1627504621139124393">"Brugerflade"</item>
+ <item msgid="8309220355268900335">"Batteri"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Stryg mod venstre for at starte den fælles vejledning"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Åbn redigeringsværktøjet til widgets"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tilføj widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Udfør"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tryk for at fastsætte en tidsplan for batterisparefunktionen"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivér, når det ser ud til, at batteriet løber tør"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nej tak"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Gem SysUI-heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"I brug"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps anvender enhedens <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Angiv standardapp til noter i Indstillinger"</string>
<string name="install_app" msgid="5066668100199613936">"Installer app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du spejle til ekstern skærm?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Al aktivitet, der i øjeblikket anvender funktioner til brug af to skærme, stoppes"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Spejl skærm"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Luk"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Skærmen er tilsluttet"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a6bc4d4..29b7018 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -253,7 +253,7 @@
<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>
<string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Neues Gerät koppeln"</string>
- <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle ansehen"</string>
+ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle anzeigen"</string>
<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_saved" msgid="7549938728928069477">"Gespeichert"</string>
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Bildschirmaufzeichnung"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Aufnahme starten"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Leistung"</item>
+ <item msgid="1627504621139124393">"Benutzeroberfläche"</item>
+ <item msgid="8309220355268900335">"Akku"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Wische nach links, um das gemeinsame Tutorial zu starten"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Widget-Editor öffnen"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Entfernen"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget hinzufügen"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Fertig"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In Verwendung"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps verwenden gerade Folgendes: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standard-Notizen-App in den Einstellungen einrichten"</string>
<string name="install_app" msgid="5066668100199613936">"App installieren"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Auf externen Bildschirm spiegeln?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Alle momentan ausgeführten Dual-Screen-Aktivitäten werden angehalten"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Bildschirm spiegeln"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Schließen"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Bildschirm verbunden"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index dd6422b..dd24ca6 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Απόδοση"</item>
+ <item msgid="1627504621139124393">"Διεπαφή χρήστη"</item>
+ <item msgid="8309220355268900335">"Μπαταρία"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Αντίθεση"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Τυπική"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Σύρετε προς τα αριστερά για να ξεκινήσετε τον κοινόχρηστο οδηγό"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Άνοιγμα προγράμ. επεξεργασίας γραφικών στοιχείων"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Κατάργηση"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Προσθήκη γραφικού στοιχείου"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Τέλος"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Πατήστε για προγραμματισμό της Εξοικονόμησης μπαταρίας"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Ενεργοποίηση όταν υπάρχει σημαντική πιθανότητα εξάντλησης της μπαταρίας"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Όχι, ευχαριστώ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Στιγμ. μνήμης SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Χρησιμοποιείται"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Οι εφαρμογές χρησιμοποιούν τις λειτουργίες <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ορίστε την προεπιλεγμένη εφαρμογή σημειώσεων στις Ρυθμίσεις"</string>
<string name="install_app" msgid="5066668100199613936">"Εγκατάσταση εφαρμογής"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Κατοπτρισμός σε εξωτερική οθόνη;"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Τυχόν τρέχουσα δραστηριότητα Dual Screen θα διακοπεί"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Κατοπτρισμός οθόνης"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Παράβλεψη"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Η οθόνη είναι συνδεδεμένη"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 46cdc0f..2d05291 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -333,6 +333,14 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performance"</item>
+ <item msgid="1627504621139124393">"User interface"</item>
+ <item msgid="8309220355268900335">"Battery"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 9acd568..c043b2c 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -333,6 +333,14 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record Issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performance"</item>
+ <item msgid="1627504621139124393">"User Interface"</item>
+ <item msgid="8309220355268900335">"Battery"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No thanks"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 46cdc0f..2d05291 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -333,6 +333,14 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performance"</item>
+ <item msgid="1627504621139124393">"User interface"</item>
+ <item msgid="8309220355268900335">"Battery"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 46cdc0f..2d05291 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -333,6 +333,14 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performance"</item>
+ <item msgid="1627504621139124393">"User interface"</item>
+ <item msgid="8309220355268900335">"Battery"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, thanks"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 9cd3014..3691b2c 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -333,6 +333,14 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Record Issue"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performance"</item>
+ <item msgid="1627504621139124393">"User Interface"</item>
+ <item msgid="8309220355268900335">"Battery"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -861,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tap to schedule Battery Saver"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Turn on when battery is likely to run out"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No thanks"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In use"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Applications are using your <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Any dual screen activity currently running will be stopped"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 4be14e9..6a4818e 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabación de pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabar error"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Rendimiento"</item>
+ <item msgid="1627504621139124393">"Interfaz de usuario"</item>
+ <item msgid="8309220355268900335">"Batería"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estándar"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza el dedo a la izquierda para iniciar el instructivo comunal"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir el editor de widget"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Agregar widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Listo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Presiona para programar el Ahorro de batería"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Actívalo cuando la batería se esté por acabar"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, gracias"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Volcar pila de SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que están usando tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la app de notas predeterminada en Configuración"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Quieres duplicar en la pantalla externa?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Se detendrá cualquier actividad en la pantalla doble que se esté ejecutando en ese momento."</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Duplicar pantalla"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Descartar"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 5d88d64..4e41db3 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -252,7 +252,7 @@
<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>
- <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Empareja un nuevo dispositivo"</string>
+ <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Emparejar un nuevo dispositivo"</string>
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
<string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabar pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema de grabación"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Rendimiento"</item>
+ <item msgid="1627504621139124393">"Interfaz de usuario"</item>
+ <item msgid="8309220355268900335">"Batería"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Estándar"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Carga completa en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza hacia la izquierda para iniciar el tutorial de la comunidad"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir editor de widgets"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Añadir widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hecho"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tocar para programar el modo Ahorro de batería"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Activar cuando sea probable que se quede sin batería"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, gracias"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Volcar montículo de SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hay aplicaciones que usan tu <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la aplicación de notas predeterminada en Ajustes"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Proyectar a pantalla externa?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Cualquier actividad de pantalla dual que se esté ejecutando en estos momentos se detendrá"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Proyectar pantalla"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Cerrar"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 258bd4d..231ddeb 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekraanisalvestus"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Alustage"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Peatage"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleemi salvestamine"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Alusta"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Peata"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Toimivus"</item>
+ <item msgid="1627504621139124393">"Kasutajaliides"</item>
+ <item msgid="8309220355268900335">"Aku"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrastsus"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Tavaline"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ühise õpetuse käivitamiseks pühkige vasakule"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vidina redaktori avamine"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Eemalda"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisa vidin"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Valmis"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Puudutage akusäästja ajastamiseks"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Lülitatakse sisse, kui aku hakkab tühjaks saama"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Tänan, ei"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kasutusel"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Rakendused kasutavad järgmisi: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Määrake seadetes märkmete vaikerakendus."</string>
<string name="install_app" msgid="5066668100199613936">"Installi rakendus"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kas peegeldada välisekraanile?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Kõik praegu käimas olevad kahe ekraaniga tegevused peatatakse"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Peegelda ekraani"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Loobu"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Kuvar on ühendatud"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 54290f7..20bf71b 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pantaila-grabaketa"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hasi"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Gelditu"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Arazo bat dago grabaketarekin"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Hasi"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabagailua"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Errendimendua"</item>
+ <item msgid="1627504621139124393">"Erabiltzaile-interfazea"</item>
+ <item msgid="8309220355268900335">"Bateria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrastea"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Arrunta"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Tutorial komuna hasteko, pasatu hatza ezkerrera"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Ireki widget-editorea"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Kendu"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Gehitu widget bat"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Eginda"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Sakatu bateria-aurreztailea noiz aktibatu programatzeko"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktibatu aurreztailea bateria agortzeko arriskua dagoenean"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ez, eskerrik asko"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Erabiltzen ari da"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikazio batzuk <xliff:g id="TYPES_LIST">%s</xliff:g> erabiltzen ari dira."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ezarri oharren aplikazio lehenetsia ezarpenetan"</string>
<string name="install_app" msgid="5066668100199613936">"Instalatu aplikazioa"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kanpoko pantailan islatu nahi duzu?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Une honetan exekutatzen ari diren pantaila bikoitzeko jarduera guztiak geldituko dira"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Islatu pantaila"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Baztertu"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Konektatutako pantaila"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 0305887..e161d7a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"عملکرد"</item>
+ <item msgid="1627504621139124393">"واسط کاربر"</item>
+ <item msgid="8309220355268900335">"باتری"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت یکدستی"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"تضاد"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"استاندارد"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ شدن • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"برای شروع آموزش گامبهگام عمومی، تند بهچپ بکشید"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"باز کردن ویرایشگر ابزارک"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"برداشتن"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"افزودن ابزارک"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"تمام"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایینپر"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"برای زمانبندی «بهینهسازی باتری» ضربه بزنید"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبهاتمام است، بهینهسازی باتری را روشن کنید"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"استفاده شده"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"برنامهها از <xliff:g id="TYPES_LIST">%s</xliff:g> شما استفاده میکنند."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
@@ -1206,9 +1207,10 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"حضور کاربر شناسایی میشود"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"برنامه پیشفرض یادداشت را در «تنظیمات» تنظیم کنید"</string>
<string name="install_app" msgid="5066668100199613936">"نصب برنامه"</string>
- <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"در نمایشگر خارجی پخش شود؟"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"همه فعالیتهای Dual Screen که درحال اجرا است متوقف خواهد شد"</string>
- <string name="mirror_display" msgid="2515262008898122928">"بازتاباندن صفحهنمایش"</string>
+ <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"روی نمایشگر خارجی قرینهسازی شود؟"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"قرینهسازی نمایشگر"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"بستن"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"نمایشگر متصل شد"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"میکروفون و دوربین"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ae92aaa..240607b 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Näytön tallennus"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Aloita"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Lopeta"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Tallenna ongelma"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Aloita"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Lopeta"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Suorituskyky"</item>
+ <item msgid="1627504621139124393">"Käyttöliittymä"</item>
+ <item msgid="8309220355268900335">"Akku"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrasti"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Tavallinen"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aloita yhteisöesittely pyyhkäisemällä vasemmalle"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Avaa widgetien muokkaaja"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Poista"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisää widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Valmis"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Ajoita virransäästö napauttamalla"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Ota käyttöön, jos akku todennäköisesti loppuu"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ei kiitos"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Luo SysUI-keon vedos"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Käytössä"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> ovat sovellusten käytössä."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Aseta oletusmuistiinpanosovellus Asetuksista"</string>
<string name="install_app" msgid="5066668100199613936">"Asenna sovellus"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Peilataanko ulkoiselle näytölle?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Kaikki käynnissä oleva kahden näytön toiminta lopetetaan"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Peilaa näyttö"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Ohita"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Näyttö yhdistetty"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index fc4c22f..82a56f5 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -265,7 +265,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Prothèses auditives"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation en cours…"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Écran de veille"</string>
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement d\'écran"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rapporter le problème"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Commencer"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performance"</item>
+ <item msgid="1627504621139124393">"Interface utilisateur"</item>
+ <item msgid="8309220355268900335">"Pile"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge en cours… • Se terminera dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer l\'écran vers la gauche pour démarrer le tutoriel communautaire"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Ouvrir l\'éditeur de widget"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Retirer"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Terminé"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Toucher pour activer la fonction Économiseur de pile"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Activer si la pile est susceptible de s\'épuiser totalement"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Non merci"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Copier mémoire SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En cours d\'utilisation"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir l\'application de prise de notes par défaut dans les Paramètres"</string>
<string name="install_app" msgid="5066668100199613936">"Installer l\'application"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer l\'écran sur un moniteur externe?"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index 8df6211..b969841 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -147,7 +147,7 @@
<item msgid="8998632451221157987">"Activé"</item>
</string-array>
<string-array name="tile_states_controls">
- <item msgid="8199009425335668294">"Non disponible"</item>
+ <item msgid="8199009425335668294">"Non disponibles"</item>
<item msgid="4544919905196727508">"Désactivées"</item>
<item msgid="3422023746567004609">"Activées"</item>
</string-array>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 462ee6c..c35f9ee 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement de l\'écran"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Début"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performances"</item>
+ <item msgid="1627504621139124393">"Interface utilisateur"</item>
+ <item msgid="8309220355268900335">"Batterie"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer vers la gauche pour démarrer le tutoriel collectif"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Ouvrir l\'éditeur de widgets"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Supprimer"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ajouter un widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"OK"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Appuyez ici pour programmer l\'économiseur de batterie"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Activer l\'économiseur de batterie si l\'autonomie restante risque d\'être insuffisante"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Non, merci"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Copier mémoire SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En cours d\'utilisation"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Des applications utilisent votre <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir une appli de notes par défaut dans les paramètres"</string>
<string name="install_app" msgid="5066668100199613936">"Installer l\'appli"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer sur l\'écran externe ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Toute activité en cours sur double écran sera interrompue."</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Écran connecté"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index bfbb825..9cf968e 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravar pantalla"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Deter"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rexistrar problema"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Deter"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Rendemento"</item>
+ <item msgid="1627504621139124393">"Interface de usuario"</item>
+ <item msgid="8309220355268900335">"Batería"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Nivel estándar"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Pasa o dedo cara á esquerda para iniciar o titorial comunitario"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir o editor de widgets"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Quitar"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engadir widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Feito"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tocar para programar a función Aforro de batería"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Activa a función se prevés que a batería pode esgotarse"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Non, grazas"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Baleirado mem. SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"En uso"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Hai aplicacións que están utilizando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Establece a aplicación de notas predeterminada en Configuración"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Queres proxectar contido nunha pantalla externa?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Deterase toda a actividade que se estea executando nunha pantalla dual"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Proxectar pantalla"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Pechar"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 4095f71..df7b203 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"પર્ફોર્મન્સ"</item>
+ <item msgid="1627504621139124393">"યૂઝર ઇન્ટરફેસ"</item>
+ <item msgid="8309220355268900335">"બૅટરી"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"કોન્ટ્રાસ્ટ"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"સ્ટૅન્ડર્ડ"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં પૂરું ચાર્જ થઈ જશે"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"કૉમ્યુનલ ટ્યૂટૉરિઅલ શરૂ કરવા માટે ડાબે સ્વાઇપ કરો"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"વિજેટ એડિટર ખોલો"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"કાઢી નાખો"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"વિજેટ ઉમેરો"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"થઈ ગયું"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"બૅટરી સેવર શેડ્યૂલ કરવા માટે ટૅપ કરો"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"જ્યારે બૅટરી સંભવિત રૂપે પૂરી થવામાં હોય ત્યારે બૅટરી સેવર ચાલુ કરો"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ના, આભાર"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ઉપયોગમાં છે"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ઍપ્લિકેશન તમારા <xliff:g id="TYPES_LIST">%s</xliff:g>નો ઉપયોગ કરી રહી છે."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"સેટિંગમાં નોંધની ડિફૉલ્ટ ઍપ સેટ કરો"</string>
<string name="install_app" msgid="5066668100199613936">"ઍપ ઇન્સ્ટૉલ કરો"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"શું બાહ્ય ડિસ્પ્લે પર મિરર કરીએ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"હાલમાં ડ્યૂઅલ સ્ક્રીન પર ચાલી રહેલી કોઈપણ પ્રવૃત્તિ રોકવામાં આવશે"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"મિરર ડિસ્પ્લે"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"છોડી દો"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Display કનેક્ટેડ છે"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 6db1f08..eec3078 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"परफ़ॉर्मेंस"</item>
+ <item msgid="1627504621139124393">"यूज़र इंटरफ़ेस"</item>
+ <item msgid="8309220355268900335">"बैटरी"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"कंट्रास्ट"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"स्टैंडर्ड"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्यूनिटी ट्यूटोरियल शुरू करने के लिए, बाईं ओर स्वाइप करें"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"विजेट एडिटर खोलें"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"हटाएं"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट जोड़ें"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"हो गया"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"बैटरी सेवर शेड्यूल करने के लिए टैप करें"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"जब बैटरी खत्म होने वाली हो तब \'बैटरी सेवर\' चालू करें"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"जी नहीं, शुक्रिया"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"इस्तेमाल किया जा रहा है"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ऐप्लिकेशन आपकी <xliff:g id="TYPES_LIST">%s</xliff:g> का इस्तेमाल कर रहे हैं."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग में जाकर, नोट लेने की सुविधा देने वाले ऐप्लिकेशन को डिफ़ॉल्ट के तौर पर सेट करें"</string>
<string name="install_app" msgid="5066668100199613936">"ऐप्लिकेशन इंस्टॉल करें"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"क्या आपको किसी बाहरी डिवाइस पर डिसप्ले करना है?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ड्यूअल स्क्रीन पर चल रही गतिविधि बंद हो जाएगी"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"डिसप्ले करें"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"खारिज करें"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"डिसप्ले कनेक्ट किया गया"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d14f9a7..f0ed400 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje zaslona"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Početak"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavi"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježi poteškoću"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Početak"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Izvedba"</item>
+ <item msgid="1627504621139124393">"Korisničko sučelje"</item>
+ <item msgid="8309220355268900335">"Baterija"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardni"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prijeđite prstom ulijevo da biste pokrenuli zajednički vodič"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otvaranje alata za uređivanje widgeta"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Dodirnite za zakazivanje štednje baterije"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Uključite kad bi se baterija mogla isprazniti"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izdvoji mem. SysUI-a"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"U upotrebi"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije upotrebljavaju <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u postavkama"</string>
<string name="install_app" msgid="5066668100199613936">"Instalacija"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li zrcaliti na vanjski zaslon?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Sva aktivnost dvostrukog zaslona koja je u tijeku će se zaustaviti"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Zrcaljenje zaslona"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Zaslon je povezan"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 1c4e00f..30d2961 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Képernyőrögzítés"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Indítás"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Leállítás"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Probléma rögzítése"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Indítás"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Leállítás"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Teljesítmény"</item>
+ <item msgid="1627504621139124393">"Kezelőfelület"</item>
+ <item msgid="8309220355268900335">"Akkumulátor"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontraszt"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Normál"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Csúsztasson gyorsan balra a közösségi útmutató elindításához"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"A modulszerkesztő megnyitása"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Eltávolítás"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Modul hozzáadása"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Kész"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Koppintson az akkumulátorkímélő mód ütemezéséhez"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Kapcsolja be, ha az akkumulátor hamarosan lemerül"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nem"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI-memória-kiírás"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Használatban"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Több alkalmazás használja a következőket: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Állítson be alapértelmezett jegyzetkészítő alkalmazást a Beállításokban"</string>
<string name="install_app" msgid="5066668100199613936">"Alkalmazás telepítése"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tükrözi a kijelzőt a külső képernyőre?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"A jelenleg futó kétképernyős tevékenységek le fognak állni"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Kijelző tükrözése"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Elvetés"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Kijelző csatlakoztatva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 1cfed6d..bbdeb16 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Աշխատանք"</item>
+ <item msgid="1627504621139124393">"Միջերես"</item>
+ <item msgid="8309220355268900335">"Մարտկոց"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Կոնտրաստ"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Սովորական"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Թերթեք ձախ՝ ուղեցույցը գործարկելու համար"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Բացել վիջեթների խմբագրիչը"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Հեռացնել"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ավելացնել վիջեթ"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Պատրաստ է"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Հպեք՝ մարտկոցի տնտեսման ռեժիմը կարգավորելու համար"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Միացնել էներգախնայումը, երբ մարտկոցի լիցքը գրեթե սպառված է"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ոչ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Օգտագործվում է"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Հավելվածներն օգտագործում են ձեր <xliff:g id="TYPES_LIST">%s</xliff:g>:"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Կարգավորեք նշումների կանխադրված հավելված Կարգավորումներում"</string>
<string name="install_app" msgid="5066668100199613936">"Տեղադրել հավելվածը"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Հայելապատճենե՞լ արտաքին էկրանին"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"Հայելապատճենել էկրանը"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Փակել"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 65c7c1d..f2ed0e7 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Perekam layar"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mulai"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Mencatat Masalah"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Mulai"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Berhenti"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performa"</item>
+ <item msgid="1627504621139124393">"Antarmuka Pengguna"</item>
+ <item msgid="8309220355268900335">"Baterai"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontras"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standar"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Geser ke kiri untuk memulai tutorial komunal"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Buka editor widget"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Hapus"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Selesai"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Ketuk untuk menjadwalkan Penghemat Baterai"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktifkan jika daya baterai kemungkinan akan habis"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Tidak, terima kasih"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Hapus Heap SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Sedang digunakan"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setel aplikasi catatan default di Setelan"</string>
<string name="install_app" msgid="5066668100199613936">"Instal aplikasi"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Cerminkan ke layar eksternal?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Aktivitas dua layar yang sedang berjalan akan dihentikan"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Cerminkan layar"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Tutup"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Layar terhubung"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 29b149d..f1f1b0f 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skjáupptaka"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hefja"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stöðva"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Vandamál tengt upptöku"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Byrja"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stöðva"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Afköst"</item>
+ <item msgid="1627504621139124393">"Notandaviðmót"</item>
+ <item msgid="8309220355268900335">"Rafhlaða"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Birtuskil"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Staðlað"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Strjúktu til vinstri til að hefja samfélagsleiðsögnina"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Opna græjuritilinn"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjarlægja"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Bæta græju við"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Lokið"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Pikka til að stilla rafhlöðusparnað"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Kveikja þegar rafhlaða er við það að klárast"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nei, takk"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Vista SysUI-gögn"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Í notkun"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Forrit eru að nota <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stilltu sjálfgefið glósuforrit í stillingunum"</string>
<string name="install_app" msgid="5066668100199613936">"Setja upp forrit"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spegla yfir á ytri skjá?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Virkni sem kann að vera í gangi á tveimur skjám verður stöðvuð"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Spegla skjá"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Hunsa"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Skjár tengdur"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 733b0b6..223e39f 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Registrazione dello schermo"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Inizia"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Interrompi"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra problema"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Avvia"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Interrompi"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Prestazioni"</item>
+ <item msgid="1627504621139124393">"Interfaccia utente"</item>
+ <item msgid="8309220355268900335">"Batteria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrasto"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Scorri a sinistra per iniziare il tutorial della community"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Apri l\'editor del widget"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Rimuovi"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Aggiungi widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Fine"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tocca per programmare il Risparmio energetico"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Attiva questa funzionalità se è probabile che la batteria si scarichi"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"No, grazie"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump heap SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In uso"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Imposta l\'app per le note predefinita nelle Impostazioni"</string>
<string name="install_app" msgid="5066668100199613936">"Installa app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vuoi eseguire il mirroring al display esterno?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualsiasi attività in modalità Dual Screen in esecuzione viene interrotta"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Esegui il mirroring del display"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Chiudi"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Display collegato"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 7072a32..44fd608 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ביצועים"</item>
+ <item msgid="1627504621139124393">"ממשק משתמש"</item>
+ <item msgid="8309220355268900335">"סוללה"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"ניגודיות"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"רגילה"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"אפשר להחליק שמאלה כדי להפעיל את המדריך המשותף"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"פתיחה של הכלי לעריכת ווידג\'טים"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"הסרה"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"הוספת ווידג\'ט"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"סיום"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"יש להקיש כדי לתזמן את מצב החיסכון בסוללה"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"מומלץ להפעיל את התכונה כשיש סבירות גבוהה שהסוללה תתרוקן"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"לא תודה"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"בשימוש"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"אפליקציות משתמשות ב<xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -938,7 +939,7 @@
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"העברה מהשוליים והצגה"</string>
<string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"הסרה"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"החלפת מצב"</string>
- <string name="quick_controls_title" msgid="6839108006171302273">"פקדי מכשירים"</string>
+ <string name="quick_controls_title" msgid="6839108006171302273">"לחצני מכשירים"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"יש לבחור אפליקציה כדי להוסיף פקדים"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{נוסף אמצעי בקרה אחד (#).}one{נוספו # אמצעי בקרה.}two{נוספו # אמצעי בקרה.}other{נוספו # אמצעי בקרה.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"הוסר"</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"צריך להגדיר את אפליקציית ברירת המחדל לפתקים ב\'הגדרות\'"</string>
<string name="install_app" msgid="5066668100199613936">"התקנת האפליקציה"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"לשקף למסך חיצוני?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"כל פעילות של מסך מפוצל שרצה כרגע תופסק"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"תצוגת מראה"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"סגירה"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"המסך מחובר"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index ca68f66..38f2dc8 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"パフォーマンス"</item>
+ <item msgid="1627504621139124393">"ユーザー インターフェース"</item>
+ <item msgid="8309220355268900335">"バッテリー"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"コントラスト"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"標準"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • フル充電まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"左にスワイプすると、コミュニティ チュートリアルが開始します"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ウィジェット エディタを開く"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"削除"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ウィジェットを追加"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"完了"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"タップしてバッテリー セーバーのスケジュールを設定"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"電池切れになる可能性が高くなると有効になります"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"いいえ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI ヒープのダンプ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"使用中"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"アプリは<xliff:g id="TYPES_LIST">%s</xliff:g>を使用しています。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"[設定] でデフォルトのメモアプリを設定してください"</string>
<string name="install_app" msgid="5066668100199613936">"アプリをインストール"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"外部ディスプレイにミラーリングしますか?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"現在実行中のデュアル スクリーンのアクティビティは停止されます"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ディスプレイをミラーリングする"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"閉じる"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ディスプレイに接続しました"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index cc6f282..bb21f3f 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ეფექტურობა"</item>
+ <item msgid="1627504621139124393">"სამომხმარებლო ინტერფეისი"</item>
+ <item msgid="8309220355268900335">"ბატარეა"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"კონტრასტი"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"სტანდარტული"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"გადაფურცლეთ მარცხნივ, რათა დაიწყოთ საერთო სახელმძღვანელო"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"გახსენით ვიჯეტის რედაქტორი"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ამოშლა"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ვიჯეტის დამატება"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"მზადაა"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"შეეხეთ ბატარეის დამზოგის დასაგეგმად"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ჩაირთოს, როცა ბატარეა დაცლის პირას არის"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"არა, გმადლობთ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI გროვის გამოტანა"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"გამოიყენება"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"აპლიკაციების მიერ გამოიყენება თქვენი <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"დააყენეთ ნაგულისხმევი შენიშვნების აპი პარამეტრებში"</string>
<string name="install_app" msgid="5066668100199613936">"აპის ინსტალაცია"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"აირეკლოს გარე ეკრანზე?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ორმაგ ეკრანზე ნებისმიერი მიმდინარე აქტივობა შეჩერდება"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ეკრანის არეკვლა"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"დახურვა"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ეკრანი დაკავშირებულია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 2a9be81..b39d5a5 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Өнімділік"</item>
+ <item msgid="1627504621139124393">"Пайдаланушы интерфейсі"</item>
+ <item msgid="8309220355268900335">"Батарея"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Контраст"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартты режим"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядталып жатыр. • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ортақ оқулықты ашу үшін солға қарай сырғытыңыз."</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Виджет редакторын ашу"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Өшіру"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет қосу"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Дайын"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Түймені түртіп, Батареяны үнемдеу режимін реттеңіз"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Батареяның заряды бітуге жақындағанда қосыңыз."</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Жоқ, рақмет"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Қолданыста"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Қолданбаларда <xliff:g id="TYPES_LIST">%s</xliff:g> пайдаланылуда."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден әдепкі жазба қолданбасын орнатыңыз."</string>
<string name="install_app" msgid="5066668100199613936">"Қолданбаны орнату"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Сыртқы экран арқылы да көрсету керек пе?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Екі экранда қазір орындалып жатқан кез келген әрекет тоқтатылады."</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Көрсету"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Жабу"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Дисплей қосылды"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index feb3af0..e989a9d 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ប្រតិបត្តិការ"</item>
+ <item msgid="1627504621139124393">"ផ្ទៃប៉ះ"</item>
+ <item msgid="8309220355268900335">"ថ្ម"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"កម្រិតរំលេចពណ៌"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ស្តង់ដារ"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"អូសទៅឆ្វេង ដើម្បីចាប់ផ្ដើមមេរៀនសហគមន៍"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"បើកកម្មវិធីកែធាតុក្រាហ្វិក"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ដកចេញ"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"បញ្ចូលធាតុក្រាហ្វិក"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"រួចរាល់"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរអ្នកប្រើ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយទាញចុះ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យទាំងអស់ក្នុងវគ្គនេះនឹងត្រូវលុប។"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ចុចដើម្បីកំណត់កាលវិភាគមុខងារសន្សំថ្ម"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"បើកនៅពេលថ្មទំនងជាអស់"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ទេ អរគុណ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"កំពុងប្រើ"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"កម្មវិធីកំពុងប្រើ <xliff:g id="TYPES_LIST">%s</xliff:g> របស់អ្នក។"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"កំណត់កម្មវិធីកំណត់ចំណាំលំនាំដើមនៅក្នុងការកំណត់"</string>
<string name="install_app" msgid="5066668100199613936">"ដំឡើងកម្មវិធី"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"បញ្ចាំងទៅផ្ទាំងអេក្រង់ខាងក្រៅឬ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"បច្ចុប្បន្ន រាល់សកម្មភាពអេក្រង់ទ្វេដែលកំពុងដំណើរការនឹងត្រូវបានបញ្ឈប់"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"បញ្ចាំងទៅផ្ទាំងអេក្រង់"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ច្រានចោល"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ផ្ទាំងអេក្រង់ត្រូវបានភ្ជាប់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index fa35189..39b1468 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ಕಾರ್ಯಕ್ಷಮತೆ"</item>
+ <item msgid="1627504621139124393">"ಬಳಕೆದಾರರ ಇಂಟರ್ಫೇಸ್"</item>
+ <item msgid="8309220355268900335">"ಬ್ಯಾಟರಿ"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"ಕಾಂಟ್ರಾಸ್ಟ್"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ಪ್ರಮಾಣಿತ"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ಸಮುದಾಯದ ಟ್ಯುಟೋರಿಯಲ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು ಎಡಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ವಿಜೆಟ್ ಎಡಿಟರ್ ಅನ್ನು ತೆರೆಯಿರಿ"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ಮುಗಿದಿದೆ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್ಡೌನ್ ಮೆನು"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಅನ್ನು ನಿಗದಿಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ಬ್ಯಾಟರಿ ಖಾಲಿಯಾಗುವ ಸಾಧ್ಯತೆ ಇದ್ದಾಗ ಆನ್ ಮಾಡಿ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ಬೇಡ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ಬಳಕೆಯಲ್ಲಿದೆ"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ನಿಮ್ಮ <xliff:g id="TYPES_LIST">%s</xliff:g> ಅನ್ನು ಆ್ಯಪ್ಗಳು ಬಳಸುತ್ತಿವೆ."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಡೀಫಾಲ್ಟ್ ಟಿಪ್ಪಣಿಗಳ ಆ್ಯಪ್ ಅನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="install_app" msgid="5066668100199613936">"ಆ್ಯಪ್ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ಬಾಹ್ಯ ಡಿಸ್ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಬೇಕೆ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ಪ್ರಸ್ತುತ ಚಾಲನೆಯಲ್ಲಿರುವ ಯಾವುದೇ ಡ್ಯುಯಲ್ ಸ್ಕ್ರೀನ್ ಚಟುವಟಿಕೆಯನ್ನು ನಿಲ್ಲಿಸಲಾಗುತ್ತದೆ"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ಮಿರರ್ ಡಿಸ್ಪ್ಲೇ"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ವಜಾಗೊಳಿಸಿ"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ಡಿಸ್ಪ್ಲೇ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3ccbed3..7bd6e6f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"성능"</item>
+ <item msgid="1627504621139124393">"사용자 인터페이스"</item>
+ <item msgid="8309220355268900335">"배터리"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"대비"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"표준"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"공동 튜토리얼을 시작하려면 왼쪽으로 스와이프하세요"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"위젯 편집기 열기"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"삭제"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"위젯 추가"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"완료"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"탭하여 절전 모드 예약"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"배터리가 소진될 것 같으면 사용 설정"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"사용 안함"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"사용 중"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"애플리케이션이 <xliff:g id="TYPES_LIST">%s</xliff:g>을(를) 사용 중입니다."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"설정에서 기본 메모 앱 설정"</string>
<string name="install_app" msgid="5066668100199613936">"앱 설치"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"외부 디스플레이로 미러링하시겠습니까?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"실행 중인 모든 듀얼 화면이 중지됩니다."</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"디스플레이 미러링"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"닫기"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"디스플레이 연결됨"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 76d3809..d4974e8 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Иштин майнаптуулугу"</item>
+ <item msgid="1627504621139124393">"Колдонуучунун интерфейси"</item>
+ <item msgid="8309220355268900335">"Батарея"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Контраст"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Кадимки"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Жалпы үйрөткүчтү иштетүү үчүн солго сүрүңүз"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Виджет түзөткүчтү ачуу"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Өчүрүү"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет кошуу"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Бүттү"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Батареяны үнөмдөгүчтүн тартибин жөндөө үчүн басыңыз"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Батареянын кубаты түгөнүп калганда, күйгүзүлсүн"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Жок, рахмат"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Колдонулууда"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Колдонмолор төмөнкүлөрдү пайдаланып жатышат: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден демейки кыска жазуулар колдонмосун тууралаңыз"</string>
<string name="install_app" msgid="5066668100199613936">"Колдонмону орнотуу"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Тышкы экранга чыгарасызбы?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Учурда иштеп жаткан бардык кош экран аракеттери токтотулат"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Тышкы экран"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Жабуу"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Экран туташтырылды"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index cf5ef3a..431b77b 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ປະສິດທິພາບ"</item>
+ <item msgid="1627504621139124393">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</item>
+ <item msgid="8309220355268900335">"ແບັດເຕີຣີ"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"ຄອນທຣາສ"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ມາດຕະຖານ"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ປັດຊ້າຍເພື່ອເລີ່ມບົດແນະນຳສ່ວນກາງ"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ເປີດຕົວແກ້ໄຂວິດເຈັດ"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ລຶບອອກ"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ເພີ່ມວິດເຈັດ"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ແລ້ວໆ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ແຕະເພື່ອຕັ້ງການເປີດຕົວປະຢັດແບັດເຕີຣີ"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ເປີດໃຊ້ເມື່ອແບັດເຕີຣີໜ້າຈະໃກ້ໝົດ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ບໍ່, ຂອບໃຈ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ກຳລັງນຳໃຊ້ຢູ່"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ແອັບພລິເຄຊັນກຳລັງໃຊ້ <xliff:g id="TYPES_LIST">%s</xliff:g> ຂອງທ່ານ."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ຕັ້ງຄ່າແອັບຈົດບັນທຶກເລີ່ມຕົ້ນໃນການຕັ້ງຄ່າ"</string>
<string name="install_app" msgid="5066668100199613936">"ຕິດຕັ້ງແອັບ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ສາຍໃສ່ຈໍສະແດງຜົນພາຍນອກບໍ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ການເຄື່ອນໄຫວໃດກໍຕາມທີ່ກຳລັງດຳເນີນການຢູ່ Dual Screen ຈະຢຸດລົງ"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ຈໍສະແດງຜົນແບບສະທ້ອນ"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ປິດໄວ້"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ເຊື່ອມຕໍ່ຈໍແລ້ວ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index ddad0be..a0eaea2 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekrano įrašas"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Pradėti"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stabdyti"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Įrašyti problemą"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Pradėti"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stabdyti"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Našumas"</item>
+ <item msgid="1627504621139124393">"Naudotojo sąsaja"</item>
+ <item msgid="8309220355268900335">"Akumuliatorius"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrastas"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Įprastas"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Perbraukite kairėn, paleistumėte bendruomenės mokomąją medžiagą"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Atidaryti valdiklio redagavimo programą"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Pašalinti"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pridėti valdiklį"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Atlikta"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Palietę planuokite akumuliatoriaus tausojimo priemonės veikimą"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Įjunkite, jei akumuliatorius gali greitai išsekti"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, ačiū"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Pat. „SysUI“ krūvą"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Naudojama"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programos naudoja: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nustatykite numatytąją užrašų programą Nustatymuose"</string>
<string name="install_app" msgid="5066668100199613936">"Įdiegti programą"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Bendrinti ekrano vaizdą išoriniame ekrane?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Bet kokia šiuo metu vykdoma dvigubo ekrano veikla bus sustabdyta"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Bendrinti ekrano vaizdą"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Atsisakyti"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekranas prijungtas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 707c656..a419f87 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekrāna ierakstīšana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Sākt"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Apturēt"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problēmas ierakstīšana"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Sākt"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Apturēt"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Veiktspēja"</item>
+ <item msgid="1627504621139124393">"Lietotāja saskarne"</item>
+ <item msgid="8309220355268900335">"Akumulators"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrasts"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standarta"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Notiek uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Velciet pa kreisi, lai palaistu kopienas pamācību."</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Atvērt logrīku redaktoru"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Noņemt"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pievienot logrīku"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gatavs"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Pieskarieties, lai iestatītu akumulatora enerģijas taupīšanas režīma grafiku"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Ieslēgt, ja akumulators var izlādēties"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nē, paldies"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Pašlaik izmanto"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Lietojumprogrammas izmanto šādas funkcijas: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Iestatījumos iestatiet noklusējuma piezīmju lietotni."</string>
<string name="install_app" msgid="5066668100199613936">"Instalēt lietotni"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vai spoguļot ārējā displejā?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Visas pašlaik aktīvās divu ekrānu darbības tiks apturētas"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Spoguļot displeju"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Nerādīt"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Pievienots displejs"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 24cd49a..ca9fe9e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Изведба"</item>
+ <item msgid="1627504621139124393">"Кориснички интерфејс"</item>
+ <item msgid="8309220355268900335">"Батерија"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Контраст"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандарден"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Повлечете налево за да го започнете заедничкото упатство"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Го отвора уредникот на виџети"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Отстранува"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додајте виџет"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Готово"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Допрете за да закажете „Штедач на батерија“"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Вклучи ако е веројатно дека батеријата ќе се испразни"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, фала"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Извади SysUI-слика"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Во употреба"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Апликациите користат <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Поставете стандардна апликација за белешки во „Поставки“"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталирајте ја апликацијата"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се преслика на надворешниот екран?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Секоја активност што тековно се извршува на Dual Screen ќе се сопре"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Отфрли"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Екранот е поврзан"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 1f153c5..5ff4656 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"പ്രകടനം"</item>
+ <item msgid="1627504621139124393">"ഉപയോക്തൃ ഇന്റര്ഫേസ്"</item>
+ <item msgid="8309220355268900335">"ബാറ്ററി"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"കോൺട്രാസ്റ്റ്"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"സ്റ്റാൻഡേർഡ്"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"കമ്മ്യൂണൽ ട്യൂട്ടോറിയൽ ആരംഭിക്കാൻ ഇടത്തോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"വിജറ്റ് എഡിറ്റർ തുറക്കുക"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"നീക്കം ചെയ്യുക"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"വിജറ്റ് ചേർക്കുക"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"പൂർത്തിയായി"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ബാറ്ററി ലാഭിക്കൽ ഷെഡ്യൂൾ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ബാറ്ററി ചാർജ് തീരാൻ സാധ്യതയുണ്ടെങ്കിൽ ഓണാക്കുക"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"വേണ്ട"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI ഹീപ്പ് ഡമ്പ് ചെയ്യുക"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ഉപയോഗത്തിലാണ്"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ആപ്പുകൾ നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്നു."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ക്രമീകരണത്തിൽ കുറിപ്പുകൾക്കുള്ള ഡിഫോൾട്ട് ആപ്പ് സജ്ജീകരിക്കുക"</string>
<string name="install_app" msgid="5066668100199613936">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യൂ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ബാഹ്യ ഡിസ്പ്ലേയിലേക്ക് മിറർ ചെയ്യണോ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"നിലവിൽ റൺ ചെയ്യുന്ന ഏതൊരു ഡ്യുവൽ സ്ക്രീൻ ആക്റ്റിവിറ്റിയും നിർത്തും"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"മിറർ ഡിസ്പ്ലേ"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ഡിസ്മിസ് ചെയ്യുക"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ഡിസ്പ്ലേ കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 91f7180..a71aca2 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Гүйцэтгэл"</item>
+ <item msgid="1627504621139124393">"Хэрэглэгчийн интерфейс"</item>
+ <item msgid="8309220355268900335">"Батарей"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Ялгарал"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандарт"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Нийтийн практик хичээлийг эхлүүлэхийн тулд зүүн тийш шударна уу"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Виджет засварлагчийг нээх"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Хасах"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет нэмэх"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Болсон"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Батарей хэмнэгч онцлогийг хуваарилахын тулд товших"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Батарей дуусах гэж байгаа үед асаана уу"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Үгүй, баярлалаа"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ашиглаж байгаа"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Аппууд таны <xliff:g id="TYPES_LIST">%s</xliff:g>-г ашиглаж байна."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Тохиргоонд тэмдэглэлийн өгөгдмөл апп тохируулна уу"</string>
<string name="install_app" msgid="5066668100199613936">"Аппыг суулгах"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Гадны дэлгэцэд тусгал үүсгэх үү?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Одоо ажиллаж буй аливаа хос дэлгэцийн үйл ажиллагааг зогсооно"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Дэлгэцийн тусгал үүсгэх"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Хаах"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Дэлгэц холбогдсон"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 39e2c5c..9dfaa00 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"परफॉर्मन्स"</item>
+ <item msgid="1627504621139124393">"यूझर इंटरफेस"</item>
+ <item msgid="8309220355268900335">"बॅटरी"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"कॉंट्रास्ट"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"साधारण"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"सामुदायिक ट्यूटोरियल सुरू करण्यासाठी डावीकडे स्वाइप करा"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"विजेट संपादक उघडा"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"काढून टाका"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट जोडा"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"पूर्ण झाले"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"बॅटरी सेव्हर शेड्यूल करण्यासाठी टॅप करा"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"बॅटरी संपण्याची शक्यता असल्यास सुरू करा"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"नाही नको"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI हीप डंप करा"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"वापरात आहे"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ॲप्लिकेशन्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग्ज मध्ये डीफॉल्ट टिपा अॅप सेट करा"</string>
<string name="install_app" msgid="5066668100199613936">"अॅप इंस्टॉल करा"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेवर मिरर करायचे आहे का?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"सध्या सुरू असलेली कोणतीही ड्युअल स्क्रीन अॅक्टिव्हिटी थांबवली जाईल"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर करा"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"डिसमिस करा"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"डिस्प्ले कनेक्ट केला आहे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 732a180..553d221 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rakam skrin"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodkan masalah"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Mula"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Hentikan"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Prestasi"</item>
+ <item msgid="1627504621139124393">"Antara Muka Pengguna"</item>
+ <item msgid="8309220355268900335">"Bateri"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontras"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Leret ke kiri untuk memulakan tutorial umum"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Buka editor widget"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Alih keluar"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Selesai"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Ketik untuk menjadualkan Penjimat Bateri"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Hidupkan apabila bateri berkemungkinan habis"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Tidak perlu"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"DumpSys"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Sedang digunakan"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Tetapkan apl nota lalai dalam Tetapan"</string>
<string name="install_app" msgid="5066668100199613936">"Pasang apl"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Paparkan pada paparan luaran?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Sebarang aktiviti dwiskrin yang sedang dijalankan akan dihentikan"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Segerakkan paparan"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Ketepikan"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Paparan disambungkan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 069a833..d3f1bad 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"စွမ်းဆောင်ရည်"</item>
+ <item msgid="1627504621139124393">"သုံးသူအတွက် ကြားခံစနစ်"</item>
+ <item msgid="8309220355268900335">"ဘက်ထရီ"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"ဆန့်ကျင်ဘက်"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ပုံမှန်"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"အများသုံးရှင်းလင်းပို့ချချက် စတင်ရန် ဘယ်သို့ပွတ်ဆွဲပါ"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ဝိဂျက်တည်းဖြတ်စနစ် ဖွင့်ရန်"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ဖယ်ရှားရန်"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ဝိဂျက်ထည့်ရန်"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ပြီးပြီ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -465,7 +467,7 @@
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"အသံတိတ် အကြောင်းကြားချက်များအားလုံးကို ရှင်းလင်းရန်"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"အကြောင်းကြားချက်များကို \'မနှောင့်ယှက်ရ\' က ခေတ္တရပ်ထားသည်"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတင်ပါ"</string>
- <string name="empty_shade_text" msgid="8935967157319717412">"အကြောင်းကြားချက်များ မရှိ"</string>
+ <string name="empty_shade_text" msgid="8935967157319717412">"အကြောင်းကြားချက် မရှိပါ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"အကြောင်းကြားချက်သစ် မရှိပါ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"အကြောင်းကြားချက်ဟောင်းကြည့်ရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ဤစက်ပစ္စည်းကို သင့်မိဘက စီမံခန့်ခွဲသည်"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"\'ဘက်ထရီ အားထိန်း\' အချိန်သတ်မှတ်ရန် အတွက် တို့ပါ"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ဘက်ထရီကုန်ခါနီးတွင် ဖွင့်ပါ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"မလိုပါ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"အသုံးပြုနေသည်"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"အပလီကေးရှင်းများက သင်၏ <xliff:g id="TYPES_LIST">%s</xliff:g> ကို အသုံးပြုနေသည်။"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"၊ "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ဆက်တင်များတွင် မူရင်းမှတ်စုများအက်ပ် သတ်မှတ်ပါ"</string>
<string name="install_app" msgid="5066668100199613936">"အက်ပ် ထည့်သွင်းရန်"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ပြင်ပဖန်သားပြင်သို့ စကရင်ပွားမလား။"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"လက်ရှိလုပ်ဆောင်နေသော Dual Screen လုပ်ဆောင်ချက်များ ရပ်သွားပါမည်"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ဖန်သားပြင်ကို စကရင်ပွားရန်"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ပယ်ရန်"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ဖန်သားပြင်ကို ချိတ်ဆက်လိုက်ပါပြီ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0b5a0b2..234e725 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skjermopptak"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stopp"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrer problem"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stopp"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Ytelse"</item>
+ <item msgid="1627504621139124393">"Brukergrensesnitt"</item>
+ <item msgid="8309220355268900335">"Batteri"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Sveip til venstre for å starte fellesveiledningen"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Åpne redigeringsverktøyet for moduler"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Fjern"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Legg til modul"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Ferdig"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Trykk for å planlegge batterisparing"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Slå på når det er sannsynlig at du går tom for batteri"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nei takk"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI-heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"I bruk"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apper bruker <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Du kan velge en standardapp for notater i Innstillinger"</string>
<string name="install_app" msgid="5066668100199613936">"Installer appen"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du speile til en ekstern skjerm?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Eventuell nåværende aktivitet på to skjermer stoppes"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Speil skjermen"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Lukk"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"En skjerm er koblet til"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 9c6ffc5..51839db 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -269,8 +269,8 @@
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"लोकेसन"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रिन सेभर"</string>
- <string name="quick_settings_camera_label" msgid="5612076679385269339">"क्यामेरा प्रयोग गर्ने अनुमति"</string>
- <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक प्रयोग गर्ने अनुमति"</string>
+ <string name="quick_settings_camera_label" msgid="5612076679385269339">"क्यामेरा एक्सेस"</string>
+ <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक एक्सेस"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध छ"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"ब्लक गरिएको छ"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"मिडिया उपकरण"</string>
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"पर्फर्मेन्स"</item>
+ <item msgid="1627504621139124393">"युजर इन्टरफेस"</item>
+ <item msgid="8309220355268900335">"ब्याट्री"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"कन्ट्रास्ट"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"डिफल्ट"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा फुल चार्ज हुने छ"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्युनल ट्युटोरियल सुरु गर्न बायाँतिर स्वाइप गर्नुहोस्"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"विजेट एडिटर खोल्नुहोस्"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"हटाउनुहोस्"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"विजेट हाल्नुहोस्"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"पूरा भयो"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ब्याट्री सेभरको समयतालिका बनाउन ट्याप गर्नुहोस्"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ब्याट्री सकिने सम्भावना भएमा अन गर्नुहोस्"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"पर्दैन, धन्यवाद"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"प्रयोगमा छ"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"एपहरूले तपाईंको <xliff:g id="TYPES_LIST">%s</xliff:g> प्रयोग गर्दै छन्।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिङमा गई नोट बनाउने डिफल्ट एप तोक्नुहोस्"</string>
<string name="install_app" msgid="5066668100199613936">"एप इन्स्टल गर्नुहोस्"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेमा मिरर गर्ने हो?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"डुअल स्क्रिनसम्बन्धी हाल चलिरहेको कुनै पनि गतिविधि रोकिने छ"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर गर्नुहोस्"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"खारेज गर्नुहोस्"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"डिस्प्ले कनेक्ट गरिएको छ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 4dae290..f66b4e1 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -253,7 +253,7 @@
<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>
<string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Nieuw apparaat koppelen"</string>
- <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alles bekijken"</string>
+ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alles tonen"</string>
<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_saved" msgid="7549938728928069477">"Opgeslagen"</string>
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Schermopname"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppen"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem vastleggen"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppen"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Op welk onderdeel van de apparaatfunctionaliteit had dit effect?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Probleemtype selecteren"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Schermopname"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Prestaties"</item>
+ <item msgid="1627504621139124393">"Gebruikersinterface"</item>
+ <item msgid="8309220355268900335">"Batterij"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standaard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe naar links om de communitytutorial te starten"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"De widget-editor openen"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Verwijderen"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget toevoegen"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Klaar"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tikken om Batterijbesparing aan te zetten"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aanzetten als de batterij waarschijnlijk leeg raakt"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nee, bedankt"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"In gebruik"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps gebruiken je <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standaard notitie-app instellen in Instellingen"</string>
<string name="install_app" msgid="5066668100199613936">"App installeren"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spiegelen naar extern scherm?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Alle dual screen-activiteiten die actief zijn, worden gestopt"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Scherm spiegelen"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Sluiten"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Scherm verbonden"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index bc599ba..0a45769 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ପରଫରମାନ୍ସ"</item>
+ <item msgid="1627504621139124393">"ୟୁଜର ଇଣ୍ଟରଫେସ"</item>
+ <item msgid="8309220355268900335">"ବେଟେରୀ"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"କଣ୍ଟ୍ରାଷ୍ଟ"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ଷ୍ଟାଣ୍ଡାର୍ଡ"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"କମ୍ୟୁନାଲ ଟ୍ୟୁଟୋରିଆଲ ଆରମ୍ଭ କରିବା ପାଇଁ ବାମକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ୱିଜେଟ ଏଡିଟର ଖୋଲନ୍ତୁ"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ୱିଜେଟ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ହୋଇଗଲା"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍ ବଦଳାନ୍ତୁ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ବ୍ୟାଟେରୀ ସେଭର୍ ଅନ୍ ହେବାର ସମୟ ସେଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ବ୍ୟାଟେରୀ ସରିବାକୁ ଥିବା ସମୟରେ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ନାହିଁ, ଥାଉ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI ହିପ୍ ଡମ୍ପ୍ କରନ୍ତୁ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ବ୍ୟବହାର ହେଉଛି"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ଆପ୍ଲିକେସନ୍ଗୁଡିକ ଆପଣଙ୍କ <xliff:g id="TYPES_LIST">%s</xliff:g> ବ୍ୟବହାର କରୁଛନ୍ତି।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ସେଟିଂସରେ ଡିଫଲ୍ଟ ନୋଟ୍ସ ଆପ ସେଟ କରନ୍ତୁ"</string>
<string name="install_app" msgid="5066668100199613936">"ଆପ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେକୁ ମିରର କରିବେ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ବର୍ତ୍ତମାନ ଚାଲୁଥିବା ଯେ କୌଣସି ଡୁଆଲ ସ୍କ୍ରିନ କାର୍ଯ୍ୟକଳାପ ବନ୍ଦ ହୋଇଯିବ"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ଡିସପ୍ଲେ ମିରର କରନ୍ତୁ"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ଖାରଜ କରନ୍ତୁ"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ଡିସପ୍ଲେ କନେକ୍ଟ କରାଯାଇଛି"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a0718ab..c83659f 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ਕਾਰਗੁਜ਼ਾਰੀ"</item>
+ <item msgid="1627504621139124393">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</item>
+ <item msgid="8309220355268900335">"ਬੈਟਰੀ"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"ਕੰਟ੍ਰਾਸਟ"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"ਮਿਆਰੀ"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ਭਾਈਚਾਰਕ ਟਿਊਟੋਰੀਅਲ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ਵਿਜੇਟ ਸੰਪਾਦਕ ਖੋਲ੍ਹੋ"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ਹਟਾਓ"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ਹੋ ਗਿਆ"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"ਬੈਟਰੀ ਸੇਵਰ ਦੀ ਸਮਾਂ-ਸੂਚੀ ਤਿਆਰ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"ਬੈਟਰੀ ਖਤਮ ਹੋਣ ਦੀ ਸੰਭਾਵਨਾ \'ਤੇ ਚਾਲੂ ਹੁੰਦਾ ਹੈ"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI ਹੀਪ ਡੰਪ ਕਰੋ"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ਵਰਤੋਂ ਵਿੱਚ"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ਐਪਲੀਕੇਸ਼ਨਾਂ ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਨੋਟ ਐਪ ਨੂੰ ਸੈੱਟ ਕਰੋ"</string>
<string name="install_app" msgid="5066668100199613936">"ਐਪ ਸਥਾਪਤ ਕਰੋ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ਕੀ ਬਾਹਰੀ ਡਿਸਪਲੇ \'ਤੇ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰਨਾ ਹੈ?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀ ਕਿਸੇ ਵੀ Dual Screen ਸਰਗਰਮੀ ਬੰਦ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ਡਿਸਪਲੇ ਨੂੰ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰੋ"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ਖਾਰਜ ਕਰੋ"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ਡਿਸਪਲੇ ਨੂੰ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 4585c1b..91f2c78 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nagrywanie ekranu"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Rozpocznij"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zatrzymaj"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Zarejestruj problem"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Rozpocznij"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zatrzymaj"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Którego aspektu korzystania z urządzenia dotyczył problem?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Wybierz typ problemu"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Nagrywanie ekranu"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Wydajność"</item>
+ <item msgid="1627504621139124393">"Interfejs"</item>
+ <item msgid="8309220355268900335">"Bateria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardowy"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aby uruchomić wspólny samouczek, przeciągnij palcem w lewo"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otwórz edytor widżetów"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Usuń"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodaj widżet"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotowe"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Kliknij, by zaplanować działanie oszczędzania baterii"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Oszczędzanie baterii włącza się, jeśli bateria jest prawie wyczerpana"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nie, dziękuję"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Zrzut stosu SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"W użyciu"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacje używają: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ustaw domyślną aplikację do obsługi notatek w Ustawieniach"</string>
<string name="install_app" msgid="5066668100199613936">"Zainstaluj aplikację"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Powielić na wyświetlaczu zewnętrznym?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Każda aktualnie uruchomiona aktywność na dwóch ekranach zostanie zatrzymana"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Powielaj obraz"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Zamknij"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Wyświetlacz podłączony"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 81ddb63..9fe372b 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Desempenho"</item>
+ <item msgid="1627504621139124393">"Interface do usuário"</item>
+ <item msgid="8309220355268900335">"Bateria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir o editor de widgets"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Concluído"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Toque para programar o recurso Economia de bateria"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Ativada quando há possibilidade de a bateria acabar"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Despejar heap SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em uso"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualquer atividade em tela dupla que esteja sendo executada no momento será interrompida"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Tela conectada"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index b519a7d..3bf9c7b 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -333,6 +333,14 @@
<string name="qs_record_issue_label" msgid="8166290137285529059">"Registar problema"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da experiência do disposit. foi afetada?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecione o tipo de problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de ecrã"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Desempenho"</item>
+ <item msgid="1627504621139124393">"Interface do utilizador"</item>
+ <item msgid="8309220355268900335">"Bateria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
@@ -861,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tocar para agendar a Poupança de bateria"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Ativar quando for provável que a bateria se esgote"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Despejar pilha SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em utilização"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"As aplicações estão a utilizar o(a) <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1201,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Predefina a app de notas nas Definições"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para o ecrã externo?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualquer atividade de Dual Screen atualmente em curso vai ser interrompida"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Espelhar ecrã"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Ignorar"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Ecrã ligado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 81ddb63..9fe372b 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravação de tela"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Parar"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Desempenho"</item>
+ <item msgid="1627504621139124393">"Interface do usuário"</item>
+ <item msgid="8309220355268900335">"Bateria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contraste"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Padrão"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Abrir o editor de widgets"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Remover"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Concluído"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Toque para programar o recurso Economia de bateria"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Ativada quando há possibilidade de a bateria acabar"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Não"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Despejar heap SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Em uso"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicativos estão usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Qualquer atividade em tela dupla que esteja sendo executada no momento será interrompida"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Tela conectada"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index bc3fbe9..18aa668 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Înregistrarea ecranului"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începe"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Oprește"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problemă legată de înregistrare"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Începe"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Oprește"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performanță"</item>
+ <item msgid="1627504621139124393">"Interfața de utilizare"</item>
+ <item msgid="8309220355268900335">"Baterie"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Glisează spre stânga pentru a începe tutorialul pentru comunitate"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Deschide editorul de widgeturi"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Elimină"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adaugă un widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gata"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Atinge pentru a programa Economisirea energiei"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Pornește dacă e probabil ca bateria să se descarce"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nu, mulțumesc"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Extrage memoria SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"În uz"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicațiile folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setează aplicația prestabilită de note din Setări"</string>
<string name="install_app" msgid="5066668100199613936">"Instalează aplicația"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Oglindești pe ecranul extern?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Orice activitate care rulează pe două ecrane va fi oprită"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Afișare în oglindă"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Închide"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Ecran conectat"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 2e8a257..51192f8 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Производительность"</item>
+ <item msgid="1627504621139124393">"Интерфейс"</item>
+ <item msgid="8309220355268900335">"Батарея"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Контрастность"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартная"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Чтобы ознакомиться с руководством, проведите по экрану влево"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Открыть редактор виджетов"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Удалить"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Добавить виджет"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Готово"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -465,7 +467,7 @@
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Отклонить все беззвучные уведомления"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"В режиме \"Не беспокоить\" уведомления заблокированы"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
- <string name="empty_shade_text" msgid="8935967157319717412">"Нет уведомлений"</string>
+ <string name="empty_shade_text" msgid="8935967157319717412">"Нет уведомлений."</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Новых уведомлений нет"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблокируйте, чтобы увидеть уведомления"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Устройством управляет один из родителей."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Нажмите, чтобы настроить режим энергосбережения"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Включать, если высока вероятность, что батарея скоро разрядится"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Нет, спасибо"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Передача SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Используется"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"В приложениях используется <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартное приложение для заметок в настройках."</string>
<string name="install_app" msgid="5066668100199613936">"Установить приложение"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублировать на внешний дисплей?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Функция одновременного использования двух экранов будет остановлена, если она активна."</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Дублировать"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Закрыть"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Экран подключен"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 2405bbf..7ade110 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"කාර්ය සාධනය"</item>
+ <item msgid="1627504621139124393">"පරිශීලක අතුරු මුහුණත"</item>
+ <item msgid="8309220355268900335">"බැටරිය"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්රකාරය"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"අසමානතාව"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"සම්මත"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"පොදු නිබන්ධනය ආරම්භ කිරීමට වමට ස්වයිප් කරන්න"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"විජට් සංස්කාරකය විවෘත කරන්න"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ඉවත් කරන්න"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"විජට්ටුව එක් කරන්න"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"නිමයි"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"බැටරි සුරැකුම කාලසටහන්ගත කිරීමට තට්ටු කරන්න"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"බැටරිය අවසන් වීමට යන විට සක්රීය කරන්න"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"එපා ස්තූතියි"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"භාවිතයේ ඇත"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"යෙදුම් ඔබේ <xliff:g id="TYPES_LIST">%s</xliff:g> භාවිත කරමින් සිටී."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"සැකසීම් තුළ පෙරනිමි සටහන් යෙදුම සකසන්න"</string>
<string name="install_app" msgid="5066668100199613936">"යෙදුම ස්ථාපනය කරන්න"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"බාහිර සංදර්ශකයට දර්පණය කරන්න ද?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"දැනට ක්රියාත්මක වන ඕනෑම ද්විත්ව තිර ක්රියාකාරකමක් නවත්වනු ඇත"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"සංදර්ශකය දර්පණය කරන්න"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"අස් කරන්න"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"සංදර්ශකය සම්බන්ධ කර ඇත"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index f53b5bf..8d760d5 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začať"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončiť"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problém s nahrávaním"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Začnite"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zastavte"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Výkon"</item>
+ <item msgid="1627504621139124393">"Používateľské rozhranie"</item>
+ <item msgid="8309220355268900335">"Batéria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Štandardný"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Potiahnutím doľava spustite komunitný návod"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Otvoriť editor miniaplikácií"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstrániť"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Pridať miniaplikáciu"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Hotovo"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Klepnutím naplánujete aktivovanie Šetriča batérie"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Zapnite, keď je batéria takmer vybitá"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nie, vďaka"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Výpis haldy SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Používa sa"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikácie používajú zoznam <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavte predvolenú aplikáciu na poznámky v Nastaveniach"</string>
<string name="install_app" msgid="5066668100199613936">"Inštalovať aplikáciu"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Chcete zrkadliť na externú obrazovku?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Akákoľvek momentálne prebiehajúca aktivita vo funkcii Dual Screen bude zastavená"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Zrkadliť obrazovku"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Zavrieť"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Obrazovka je pripojená"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ef56c4b..4ae80ef 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snemanje zaslona"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začni"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ustavi"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Snemanje težave"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Začetek"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ustavitev"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto težave"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Učinkovitost delovanja"</item>
+ <item msgid="1627504621139124393">"Uporabniški vmesnik"</item>
+ <item msgid="8309220355268900335">"Baterija"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standardni"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Povlecite levo, da zaženete vadnico za skupnost"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Odpiranje urejevalnika pripomočkov"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Odstrani"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajanje pripomočka"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Končano"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Dotaknite se za načrtovanje varčevanja z energijo baterije"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Vklop, če je verjetno, da se bo baterija izpraznila"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ne, hvala"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Izvoz kopice SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"V uporabi"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacije uporabljajo <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavite privzeto aplikacijo za zapiske v nastavitvah."</string>
<string name="install_app" msgid="5066668100199613936">"Namesti aplikacijo"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite zrcaliti na zunanji zaslon?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Vse dejavnosti na dveh zaslonih, ki se trenutno izvajajo, bodo ustavljene."</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Zrcali zaslon"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Opusti"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Zaslon je povezan"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b8824c7..8372703 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Regjistrimi i ekranit"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Nis"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ndalo"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Regjistro problemin"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Nis"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ndalo"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performanca"</item>
+ <item msgid="1627504621139124393">"Ndërfaqja e përdoruesit"</item>
+ <item msgid="8309220355268900335">"Bateria"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrasti"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Rrëshqit shpejt majtas për të filluar udhëzuesin e përbashkët"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Hap modifikuesin e miniaplikacionit"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Hiq"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Shto miniaplikacionin"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"U krye"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Trokit për të planifikuar \"Kursyesin e baterisë\""</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivizoje kur bateria mund të mbarojë"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Jo"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Hidh grumbullin SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Në përdorim"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikacionet po përdorin <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Cakto aplikacionin e parazgjedhur të shënimeve te \"Cilësimet\""</string>
<string name="install_app" msgid="5066668100199613936">"Instalo aplikacionin"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Të pasqyrohet në ekranin e jashtëm?"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"Pasqyro ekranin"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Hiq"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1eb4519..1bda1db 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Учинак"</item>
+ <item msgid="1627504621139124393">"Кориснички интерфејс"</item>
+ <item msgid="8309220355268900335">"Батерија"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Контраст"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандардно"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Пуни се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Превуците улево да бисте започели заједнички водич"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Отвори уређивач виџета"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Уклони"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додај виџет"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Готово"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Додирните да бисте направили распоред за уштеду батерије"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Укључите ако ће батерија вероватно да се испразни"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Не, хвала"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Издвоји SysUI мем."</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"У употреби"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Апликације користе <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Подесите подразумевану апликацију за белешке у Подешавањима"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталирај апликацију"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Желите ли да пресликате на спољњи екран?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Биће заустављена свака активност двојног екрана која је у току"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Одбаци"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Екран је повезан"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c998e6d..8cfafcf 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skärminspelning"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starta"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppa"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrera problem"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Starta"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppa"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Vilken enhetsupplevelse påverkades?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Välj problemtyp"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skärminspelning"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Prestanda"</item>
+ <item msgid="1627504621139124393">"Användargränssnitt"</item>
+ <item msgid="8309220355268900335">"Batteri"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Svep åt vänster för att börja med gruppguiden"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Öppna widgetredigeraren"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Ta bort"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lägg till widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Klar"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Tryck för att skapa ett schema för batterisparläget"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivera när batteriet håller på att ta slut"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nej tack"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI-heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Används"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"<xliff:g id="TYPES_LIST">%s</xliff:g> används av appar."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1115,7 +1116,7 @@
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Du ansluts inte till wifi automatiskt för närvarande"</string>
- <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Se alla"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"I syfte att förbättra upplevelsen med enheten kan appar och tjänster fortfarande söka efter wifi-nätverk när som helst, även om wifi har inaktiverats. Du kan ändra detta i inställningarna för wifi-sökning. "<annotation id="link">"Ändra"</annotation></string>
<string name="turn_off_airplane_mode" msgid="8425587763226548579">"Inaktivera flygplansläge"</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ställ in en standardapp för anteckningar i inställningarna"</string>
<string name="install_app" msgid="5066668100199613936">"Installera appen"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vill du spegla till extern skärm?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Pågående Dual Screen-aktivitet stoppas"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Spegla skärm"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Ignorera"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Skärm har anslutits"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index fc742d8..286e65a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekodi ya skrini"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Anza kurekodi"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Acha kurekodi"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Hitilafu ya Kurekodi"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Anza"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Simamisha"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Utendaji"</item>
+ <item msgid="1627504621139124393">"Kiolesura cha Mtumiaji"</item>
+ <item msgid="8309220355268900335">"Betri"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Utofautishaji"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Kawaida"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Telezesha kidole kushoto ili uanze mafunzo ya pamoja"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Fungua kihariri cha wijeti"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Ondoa"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ongeza wijeti"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Nimemaliza"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Gusa ili uratibu wakati wa kuwasha Kiokoa Betri"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Washa wakati betri inakaribia kuisha"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Hapana"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Inatumika"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Programu zinatumia <xliff:g id="TYPES_LIST">%s</xliff:g> yako."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Teua programu chaguomsingi ya madokezo katika Mipangilio"</string>
<string name="install_app" msgid="5066668100199613936">"Sakinisha programu"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ungependa kuonyesha kwenye skrini ya nje?"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"Akisi skrini"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Ondoa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index afba7a92..aab713f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"செயல்திறன்"</item>
+ <item msgid="1627504621139124393">"பயனர் இடைமுகம்"</item>
+ <item msgid="8309220355268900335">"பேட்டரி"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"ஒளி மாறுபாடு"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"இயல்புநிலை"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுவதும் சார்ஜாகும்"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"சமூகப் பயிற்சியைத் தொடங்க இடதுபுறம் ஸ்வைப் செய்யுங்கள்"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"விட்ஜெட் எடிட்டரைத் திறக்கும்"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"அகற்றும்"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"விட்ஜெட்டைச் சேர்"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"முடிந்தது"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"பேட்டரி சேமிப்பானை ஆன் செய்வது தொடர்பாகத் திட்டமிட, தட்டவும்"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"பேட்டரி தீர்ந்துபோகும் நிலையில் இருக்கும் போது ஆன் செய்யப்படும்"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"வேண்டாம்"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"உபயோகத்தில் உள்ளது"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"உங்கள் <xliff:g id="TYPES_LIST">%s</xliff:g> ஆகியவற்றை ஆப்ஸ் பயன்படுத்துகின்றன."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"குறிப்பு எடுப்பதற்கான இயல்புநிலை ஆப்ஸை அமைப்புகளில் அமையுங்கள்"</string>
<string name="install_app" msgid="5066668100199613936">"ஆப்ஸை நிறுவுங்கள்"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"வெளிப்புறக் காட்சிக்கு மிரர் செய்யவா?"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"டிஸ்பிளேயை மிரர் செய்"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"வேண்டாம்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 6621dae..2536a96 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"పనితీరు"</item>
+ <item msgid="1627504621139124393">"యూజర్ ఇంటర్ఫేస్"</item>
+ <item msgid="8309220355268900335">"బ్యాటరీ"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"కాంట్రాస్ట్"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"స్టాండర్డ్"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"కమ్యూనల్ ట్యుటోరియల్ను ప్రారంభించడానికి ఎడమ వైపునకు స్వైప్ చేయండి"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"విడ్జెట్ ఎడిటర్ను తెరవండి"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"తీసివేయండి"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"విడ్జెట్ను జోడించండి"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"పూర్తయింది"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్డౌన్ మెనూ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"బ్యాటరీ సేవర్ని షెడ్యూల్ చేయడానికి నొక్కండి"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"బ్యాటరీ ఛార్జింగ్ పూర్తిగా అయిపోతున్న తరుణంలో ఆన్ చేస్తుంది"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"వద్దు, ధన్యవాదాలు"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"డంప్ SysUI హీప్"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"వినియోగంలో ఉంది"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"అప్లికేషన్లు మీ <xliff:g id="TYPES_LIST">%s</xliff:g>ని ఉపయోగిస్తున్నాయి."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"సెట్టింగ్లలో ఆటోమేటిక్గా ఉండేలా ఒక నోట్స్ యాప్ను సెట్ చేసుకోండి"</string>
<string name="install_app" msgid="5066668100199613936">"యాప్ను ఇన్స్టాల్ చేయండి"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ఎక్స్టర్నల్ డిస్ప్లేకి మిర్రర్ చేయాలా?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"ప్రస్తుతం రన్ అవుతున్న Dual Screen యాక్టివిటీ ఏదైనా ఉంటే, అది ఆపివేయబడుతుంది"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"మిర్రర్ డిస్ప్లే"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"విస్మరించండి"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"డిస్ప్లే కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 8c24e4b..3c96e30 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"ประสิทธิภาพ"</item>
+ <item msgid="1627504621139124393">"อินเทอร์เฟซผู้ใช้"</item>
+ <item msgid="8309220355268900335">"แบตเตอรี่"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"คอนทราสต์"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"มาตรฐาน"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ปัดไปทางซ้ายเพื่อเริ่มบทแนะนำส่วนกลาง"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"เปิดเครื่องมือแก้ไขวิดเจ็ต"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"นำออก"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"เพิ่มวิดเจ็ต"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"เสร็จสิ้น"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"แตะเพื่อตั้งเวลาโหมดประหยัดแบตเตอรี่"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"เปิดเมื่อมีแนวโน้มว่าแบตเตอรี่จะหมด"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"ไม่เป็นไร"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ใช้งานอยู่"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"หลายแอปพลิเคชันใช้<xliff:g id="TYPES_LIST">%s</xliff:g>ของคุณอยู่"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"กำหนดแอปการจดบันทึกเริ่มต้นในการตั้งค่า"</string>
<string name="install_app" msgid="5066668100199613936">"ติดตั้งแอป"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"มิเรอร์ไปยังจอแสดงผลภายนอกไหม"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"กิจกรรมที่ทำอยู่บน Dual Screen จะหยุดลง"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"มิเรอร์จอแสดงผล"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"ปิด"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"เชื่อมต่อจอแสดงผลแล้ว"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f0b9d1f..8fc5b6b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pag-record ng screen"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Magsimula"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ihinto"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Mag-record ng Isyu"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Magsimula"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ihinto"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ano\'ng naapektuhang parte ng experience sa device?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Piliin ang uri ng isyu"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pag-record ng screen"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performance"</item>
+ <item msgid="1627504621139124393">"User Interface"</item>
+ <item msgid="8309220355268900335">"Baterya"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Contrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standard"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Mag-swipe pakaliwa para simulan ang communal na tutorial"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Buksan ang editor ng widget"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Alisin"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Magdagdag ng widget"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Tapos na"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"I-tap para iiskedyul ang Pantipid ng Baterya"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"I-on kapag malamang na maubos ang baterya"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Hindi, salamat na lang"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Ginagamit"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ginagamit ng mga application ang iyong <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Magtakda ng default na app sa pagtatala sa Mga Setting"</string>
<string name="install_app" msgid="5066668100199613936">"I-install ang app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"I-mirror sa external na display?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Ihihinto ang anumang aktibidad sa dual screen na kasalukuyang tumatakbo."</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"I-mirror ang display"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"I-dismiss"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Naikonekta ang display"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f9d90d4..a4083ef 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran kaydı"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlat"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Durdur"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Sorunu Kaydedin"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Başlayın"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Durdurun"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Performans"</item>
+ <item msgid="1627504621139124393">"Kullanıcı Arayüzü"</item>
+ <item msgid="8309220355268900335">"Pil"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standart"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ortak eğitimi başlatmak için sola kaydırın"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Widget düzenleyiciyi açın"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Kaldır"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget ekle"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Bitti"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Pil Tasarrufunu programlamak için dokunun"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Piliniz bitecek gibiyse bu özelliği açın"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Hayır, teşekkürler"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI Yığın Dökümü"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kullanımda"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Uygulamalar şunları kullanıyor: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlar\'ı kullanarak varsayılan notlar ayarlayın"</string>
<string name="install_app" msgid="5066668100199613936">"Uygulamayı yükle"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Harici ekrana yansıtılsın mı?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Şu anda devam eden tüm Dual Screen etkinlikleri durdurulacak"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Ekranı yansıt"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Kapat"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran bağlandı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c5211e8..5e0f2c2 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Продуктивність"</item>
+ <item msgid="1627504621139124393">"Інтерфейс користувача"</item>
+ <item msgid="8309220355268900335">"Акумулятор"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Контраст"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Стандартний"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Проведіть пальцем уліво, щоб відкрити спільний навчальний посібник"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Відкрити редактор віджетів"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Видалити"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додати віджет"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Готово"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Торкніться, щоб налаштувати режим енергозбереження"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Вмикати, коли заряд акумулятора закінчується"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Ні, дякую"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Використовується"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Додатки використовують <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Призначте стандартний додаток для нотаток у налаштуваннях"</string>
<string name="install_app" msgid="5066668100199613936">"Установити додаток"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублювати на зовнішньому екрані?"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"Дублювати екран"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Закрити"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 01995c8..c28e064 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"کارکردگی"</item>
+ <item msgid="1627504621139124393">"یوزر انٹرفیس"</item>
+ <item msgid="8309220355268900335">"بیٹری"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"کنٹراسٹ"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"معیاری"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"کمیونل ٹیوٹوریل شروع کرنے کے لیے بائیں سوائپ کریں"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"ویجیٹ ایڈیٹر کو کھولیں"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"ہٹائیں"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"ویجیٹ شامل کریں"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"ہو گیا"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"بیٹری سیور شیڈول کرنے کے لیے تھپتھپائیں"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"جب بیٹری کے ختم ہونے کا امکان ہو تو آن کریں"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"نہیں شکریہ"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"زیر استعمال"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ایپلیکیشنز آپ کی <xliff:g id="TYPES_LIST">%s</xliff:g> کا استعمال کر رہی ہیں۔"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ترتیبات میں ڈیفالٹ نوٹس ایپ سیٹ کریں"</string>
<string name="install_app" msgid="5066668100199613936">"ایپ انسٹال کریں"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"بیرونی ڈسپلے پر مرر کریں؟"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"فی الحال چلنے والی Dual Screen کی کوئی بھی سرگرمی روک دی جائے گی"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"ڈسپلے کو مرر کریں"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"برخاست کریں"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"ڈسپلے منسلک ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index bf48146..e032c76 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yozuvi"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Boshlash"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Toʻxtatish"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Yozib olishda xato"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Boshlash"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Toʻxtatish"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Qurilma ishlashining qaysi qismiga taʼsir qildi?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Muammo turini tanlang"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran yozuvi"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Unumdorlik"</item>
+ <item msgid="1627504621139124393">"Foydalanuvchi interfeysi"</item>
+ <item msgid="8309220355268900335">"Batareya"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Kontrast"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Standart"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Qoʻllanma bilan tanishish uchun chapga suring"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vidjet muharririni ochish"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Olib tashlash"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Vidjet kiritish"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Tayyor"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Quvvat tejash rejimini rejalashtirish uchun bosing"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Batareya quvvati kamayishi aniqlanganda yoqilsin"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Kerak emas"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Foydalanilmoqda"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Ilovalarda ishlatilmoqda: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standart qaydlar ilovasini Sozlamalar orqali tanlang"</string>
<string name="install_app" msgid="5066668100199613936">"Ilovani oʻrnatish"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tashqi displeyda aks ettirilsinmi?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Joriy ishga tushgan ikki ekranli faollik toʻxtatiladi"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Displeyni aks ettirish"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Yopish"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Displey ulandi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index a70c8ad..b87619e 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -269,7 +269,7 @@
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string>
<string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Trình bảo vệ màn hình"</string>
- <string name="quick_settings_camera_label" msgid="5612076679385269339">"Truy cập máy ảnh"</string>
+ <string name="quick_settings_camera_label" msgid="5612076679385269339">"Quyền truy cập camera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Quyền truy cập micrô"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Được phép"</string>
<string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Bị chặn"</string>
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ghi màn hình"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Bắt đầu"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dừng"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi lại vấn đề"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Bắt đầu"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dừng"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Hiệu suất"</item>
+ <item msgid="1627504621139124393">"Giao diện người dùng"</item>
+ <item msgid="8309220355268900335">"Pin"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Độ tương phản"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Chuẩn"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Vuốt sang trái để bắt đầu xem hướng dẫn chung"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Mở trình chỉnh sửa tiện ích"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Xoá"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Thêm tiện ích"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Xong"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Nhấn để lên lịch Trình tiết kiệm pin"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Bật khi pin sắp hết"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Không, cảm ơn"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Trích xuất bộ nhớ SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Đang được sử dụng"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Các ứng dụng đang dùng <xliff:g id="TYPES_LIST">%s</xliff:g> của bạn."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Đặt ứng dụng ghi chú mặc định trong phần Cài đặt"</string>
<string name="install_app" msgid="5066668100199613936">"Cài đặt ứng dụng"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Phản chiếu sang màn hình ngoài?"</string>
- <!-- no translation found for connected_display_dialog_dual_display_stop_warning (2917631104216376315) -->
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
<skip />
<string name="mirror_display" msgid="2515262008898122928">"Phản chiếu màn hình"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Đóng"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index abc2e31..43dfbe8 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -298,7 +298,7 @@
<string name="quick_settings_connecting" msgid="2381969772953268809">"正在连接…"</string>
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"热点"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"正在开启…"</string>
- <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"流量节省程序已开启"</string>
+ <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"省流模式已开启"</string>
<string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# 部设备}other{# 部设备}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"手电筒"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"相机正在使用中"</string>
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"性能"</item>
+ <item msgid="1627504621139124393">"界面"</item>
+ <item msgid="8309220355268900335">"电池"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"对比度"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"标准"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑动即可启动公共教程"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"打开微件编辑器"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"添加微件"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"完成"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -609,7 +611,7 @@
<string name="notification_channel_summary_low" msgid="4860617986908931158">"不发出提示音,也不振动"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"不发出提示音,也不振动;显示在对话部分的靠下位置"</string>
<string name="notification_channel_summary_default" msgid="777294388712200605">"可能会响铃或振动,取决于设备设置"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"可能会响铃或振动,取决于设备设置。默认情况下,来自<xliff:g id="APP_NAME">%1$s</xliff:g>的对话会以对话泡的形式显示。"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"可能会响铃或振动,取决于设备设置。默认情况下,来自<xliff:g id="APP_NAME">%1$s</xliff:g>的对话会以消息气泡的形式显示。"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"让系统决定是否应让设备在收到此通知时发出提示音或振动"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>状态</b>:已提升为“默认”"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>状态</b>:已降低为“静音”"</string>
@@ -637,7 +639,7 @@
<string name="notification_more_settings" msgid="4936228656989201793">"更多设置"</string>
<string name="notification_app_settings" msgid="8963648463858039377">"自定义"</string>
<string name="notification_conversation_bubble" msgid="2242180995373949022">"显示气泡"</string>
- <string name="notification_conversation_unbubble" msgid="6908427185031099868">"移除对话泡"</string>
+ <string name="notification_conversation_unbubble" msgid="6908427185031099868">"移除消息气泡"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g><xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"通知设置"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"通知延后选项"</string>
@@ -736,8 +738,8 @@
<string name="accessibility_long_click_tile" msgid="210472753156768705">"打开“设置”"</string>
<string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"已连接到耳机"</string>
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"已连接到耳机"</string>
- <string name="data_saver" msgid="3484013368530820763">"流量节省程序"</string>
- <string name="accessibility_data_saver_on" msgid="5394743820189757731">"流量节省程序已开启"</string>
+ <string name="data_saver" msgid="3484013368530820763">"省流模式"</string>
+ <string name="accessibility_data_saver_on" msgid="5394743820189757731">"省流模式已开启"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"开启"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"关闭"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"不可用"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"点按即可预设省电模式"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"在电池电量可能会耗尽时,系统会开启此模式"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"不用了"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"转储 SysUI 堆"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"正在使用"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多个应用正在使用您的<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在设置中设置默认记事应用"</string>
<string name="install_app" msgid="5066668100199613936">"安装应用"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"镜像到外接显示屏?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"当前正在进行的任何双屏幕活动都将会停止"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"镜像到显示屏"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"关闭"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"显示屏已连接"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index ac40c48..784f667 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"效能"</item>
+ <item msgid="1627504621139124393">"使用者介面"</item>
+ <item msgid="8309220355268900335">"電池"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"對比"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"標準"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可開始共用教學課程"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"開啟小工具編輯器"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"新增小工具"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"完成"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"輕按即可預定慳電模式自動開啟時間"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"在電池電量可能耗盡前啟用「慳電模式」"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"不用了,謝謝"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"使用中"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設筆記應用程式"</string>
<string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要鏡像投射至外部顯示屏嗎?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"目前進行的雙螢幕活動都會停止"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"已連接顯示屏"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index cc34a4a..15da239 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -330,12 +330,17 @@
<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>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <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_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-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"效能"</item>
+ <item msgid="1627504621139124393">"使用者介面"</item>
+ <item msgid="8309220355268900335">"電池"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"對比"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"標準"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可啟動通用教學課程"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"開啟小工具編輯器"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"移除"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"新增小工具"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"完成"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"輕觸即可排定省電模式自動開啟的情況"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"在電池電量即將耗盡時開啟"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"不用了,謝謝"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"使用中"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設記事應用程式"</string>
<string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要以鏡像方式投放至外部螢幕嗎?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"目前進行的雙螢幕活動都會停止"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"螢幕已連結"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 83fc628..31cdca9 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -330,12 +330,17 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Irekhodi lesikrini"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Qala"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Misa"</string>
- <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
- <skip />
- <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
- <skip />
- <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
- <skip />
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekhoda Inkinga"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Qala"</string>
+ <string name="qs_record_issue_stop" msgid="3531747965741982657">"Misa"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string>
+ <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string>
+ <string-array name="qs_record_issue_types">
+ <item msgid="2947988124014085798">"Ukusebenza"</item>
+ <item msgid="1627504621139124393">"Okusetshenziswa Kubonwa"</item>
+ <item msgid="8309220355268900335">"Ibhethri"</item>
+ </string-array>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string>
<string name="quick_settings_contrast_label" msgid="988087460210159123">"Ukugqama"</string>
<string name="quick_settings_contrast_standard" msgid="2538227821968061832">"Okujwayelekile"</string>
@@ -408,12 +413,9 @@
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Iyashaja • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swayiphela kwesokunxele ukuze uqale okokufundisa komphakathi"</string>
<string name="button_to_open_widget_editor" msgid="5599945944349057600">"Vula isihleli sewijethi"</string>
- <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
- <skip />
- <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
- <skip />
- <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
- <skip />
+ <string name="button_to_remove_widget" msgid="3948204829181214098">"Susa"</string>
+ <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Engeza iwijethi"</string>
+ <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Kwenziwe"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -867,7 +869,6 @@
<string name="auto_saver_title" msgid="6873691178754086596">"Thepha ukuze ushejuli isilondolozi sebhethri"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Vula uma ibhethri sekungenzeka liphele"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Cha ngiyabonga"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"I-Dump SysUI Heap"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"Kuyasebenza"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Izinhlelo zokusebenza zisebenzisa i-<xliff:g id="TYPES_LIST">%s</xliff:g> yakho."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1207,7 +1208,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setha i-app yamanothi azenzakalelayo Kumsethingi"</string>
<string name="install_app" msgid="5066668100199613936">"Faka i-app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Fanisa nesibonisi sangaphandle?"</string>
- <string name="connected_display_dialog_dual_display_stop_warning" msgid="2917631104216376315">"Noma yimuphi umsebenzi wezikrini ezimbili ezisebenzayo uzomiswa"</string>
+ <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
+ <skip />
<string name="mirror_display" msgid="2515262008898122928">"Isibonisi sokufanisa"</string>
<string name="dismiss_dialog" msgid="2195508495854675882">"Chitha"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Isibonisi sixhunyiwe"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 90cc1fb..e01a2aa 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -480,9 +480,6 @@
This name is in the ComponentName flattened format (package/class) -->
<string name="config_remoteCopyPackage" translatable="false"></string>
- <!-- On debuggable builds, alert the user if SystemUI PSS goes over this number (in kb) -->
- <integer name="watch_heap_limit">256000</integer>
-
<!-- SystemUI Plugins that can be loaded on user builds. -->
<string-array name="config_pluginAllowlist" translatable="false">
<item>com.android.systemui</item>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0267454..ee89ede 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -274,6 +274,10 @@
<!-- Side padding on the side of notifications -->
<dimen name="notification_side_paddings">16dp</dimen>
+ <!-- Starting translateY offset of the HUN appear and disappear animations. Indicates
+ the amount by the view is positioned above the screen before the animation starts. -->
+ <dimen name="heads_up_appear_y_above_screen">32dp</dimen>
+
<!-- padding between the heads up and the statusbar -->
<dimen name="heads_up_status_bar_padding">8dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 6cb2d45..6e035e8 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -225,6 +225,8 @@
<item type="id" name="communal_tutorial_indicator" />
<item type="id" name="nssl_placeholder_barrier_bottom" />
<item type="id" name="ambient_indication_container" />
+ <item type="id" name="status_view_media_container" />
+ <item type="id" name="smart_space_barrier_bottom" />
<!-- Privacy dialog -->
<item type="id" name="privacy_dialog_close_app_button" />
@@ -260,6 +262,11 @@
<item type="id" name="device_entry_icon_fg" />
<item type="id" name="device_entry_icon_bg" />
- <!--Id for the device-entry UDFPS icon that lives in the alternate bouncer. -->
+ <!--Id for the device-entry UDFPS related views that live in the alternate bouncer. -->
<item type="id" name="alternate_bouncer_udfps_icon_view" />
+ <item type="id" name="alternate_bouncer_udfps_accessibility_overlay" />
+
+ <!-- Id for the udfps accessibility overlay -->
+ <item type="id" name="udfps_accessibility_overlay" />
+ <item type="id" name="udfps_accessibility_overlay_top_guideline" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e10925d..e7eb984 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2397,10 +2397,6 @@
<!-- URl of the webpage that explains battery saver. -->
<string name="help_uri_battery_saver_learn_more_link_target" translatable="false"></string>
- <!-- Name for a quick settings tile, used only by platform developers, to extract the SystemUI process memory and send it to another
- app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
- <string name="heap_dump_tile_name">Dump SysUI Heap</string>
-
<!-- Title for the privacy indicators dialog, only appears as part of a11y descriptions [CHAR LIMIT=NONE] -->
<string name="ongoing_privacy_dialog_a11y_title">In use</string>
@@ -3269,7 +3265,7 @@
<!--- Title of the dialog appearing when an external display is connected, asking whether to start mirroring [CHAR LIMIT=NONE]-->
<string name="connected_display_dialog_start_mirroring">Mirror to external display?</string>
<!--- Body of the mirroring dialog, shown when dual display is enabled. This signals that enabling mirroring will stop concurrent displays on a foldable device. [CHAR LIMIT=NONE]-->
- <string name="connected_display_dialog_dual_display_stop_warning">Any dual screen activity currently running will be stopped</string>
+ <string name="connected_display_dialog_dual_display_stop_warning">Your inner display will be mirrored. Your front display will be turned off.</string>
<!--- Label of the "enable display" button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
<string name="mirror_display">Mirror display</string>
<!--- Label of the dismiss button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 5b59e7d..2b41178 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -34,6 +34,9 @@
srcs: [
":statslog-SystemUI-java-gen",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
android_library {
@@ -70,6 +73,9 @@
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
kotlincflags: ["-Xjvm-default=all"],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_library {
@@ -81,6 +87,9 @@
static_kotlin_stdlib: false,
java_version: "1.8",
min_sdk_version: "current",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_library {
@@ -100,4 +109,7 @@
},
java_version: "1.8",
min_sdk_version: "current",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SystemUI/shared/res/values/ids.xml b/packages/SystemUI/shared/res/values/ids.xml
new file mode 100644
index 0000000..1ff2f0e
--- /dev/null
+++ b/packages/SystemUI/shared/res/values/ids.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- ID of the smartspace card view. -->
+ <item type="id" name="bc_smartspace_view" />
+ <!-- ID of the smartspace date view. -->
+ <item type="id" name="date_smartspace_view" />
+ <!-- ID of the smartspace weather view. -->
+ <item type="id" name="weather_smartspace_view" />
+</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 92f66902..387f2e1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -101,7 +101,7 @@
}
/** Alerts listener and plugin that the plugin has been created. */
- public void onCreate() {
+ public synchronized void onCreate() {
boolean loadPlugin = mListener.onPluginAttached(this);
if (!loadPlugin) {
if (mPlugin != null) {
@@ -128,7 +128,7 @@
}
/** Alerts listener and plugin that the plugin is being shutdown. */
- public void onDestroy() {
+ public synchronized void onDestroy() {
logDebug("onDestroy");
unloadPlugin();
mListener.onPluginDetached(this);
@@ -143,12 +143,13 @@
/**
* Loads and creates the plugin if it does not exist.
*/
- public void loadPlugin() {
+ public synchronized void loadPlugin() {
if (mPlugin != null) {
logDebug("Load request when already loaded");
return;
}
+ // Both of these calls take about 1 - 1.5 seconds in test runs
mPlugin = mPluginFactory.createPlugin();
mPluginContext = mPluginFactory.createPluginContext();
if (mPlugin == null || mPluginContext == null) {
@@ -171,7 +172,7 @@
*
* This will free the associated memory if there are not other references.
*/
- public void unloadPlugin() {
+ public synchronized void unloadPlugin() {
if (mPlugin == null) {
logDebug("Unload request when already unloaded");
return;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index d8c1e41..131eb6b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -367,7 +367,7 @@
/**
* Corner radius that should be used on windows in order to cover the display.
* These values are expressed in pixels because they should not respect display or font
- * scaling, this means that we don't have to reload them on config changes.
+ * scaling. The corner radius may change when folding/unfolding the device.
*/
public static float getWindowCornerRadius(Context context) {
return ScreenDecorationsUtils.getWindowCornerRadius(context);
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 1ee58de..5e76801 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -41,6 +41,7 @@
import com.android.systemui.util.settings.SecureSettings
import java.io.PrintWriter
import javax.inject.Inject
+import dagger.Lazy
/**
* Handles active unlock settings changes.
@@ -51,6 +52,7 @@
private val secureSettings: SecureSettings,
private val contentResolver: ContentResolver,
private val selectedUserInteractor: SelectedUserInteractor,
+ private val keyguardUpdateMonitor: Lazy<KeyguardUpdateMonitor>,
dumpManager: DumpManager
) : Dumpable {
@@ -96,7 +98,6 @@
UNDER_DISPLAY_FINGERPRINT(3),
}
- var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
private var requestActiveUnlockOnWakeup = false
private var requestActiveUnlockOnUnlockIntent = false
private var requestActiveUnlockOnBioFail = false
@@ -316,7 +317,7 @@
return false
}
- keyguardUpdateMonitor?.let {
+ keyguardUpdateMonitor.get().let {
val anyFaceEnrolled = it.isFaceEnabledAndEnrolled
val anyFingerprintEnrolled = it.isUnlockWithFingerprintPossible(
selectedUserInteractor.getSelectedUserId())
@@ -369,13 +370,13 @@
}")
pw.println("Current state:")
- keyguardUpdateMonitor?.let {
+ keyguardUpdateMonitor.get().let {
pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
"${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
pw.println(" isFaceEnabledAndEnrolled=${it.isFaceEnabledAndEnrolled}")
pw.println(" fpUnlockPossible=${
it.isUnlockWithFingerprintPossible(selectedUserInteractor.getSelectedUserId())}")
pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}")
- } ?: pw.println(" keyguardUpdateMonitor is uninitialized")
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 76abad8..bcc2044 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -45,12 +45,11 @@
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
import com.android.systemui.log.core.LogLevel.DEBUG
-import com.android.systemui.log.dagger.KeyguardLargeClockLog
-import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockTickRate
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.WeatherData
@@ -91,117 +90,120 @@
private val context: Context,
@Main private val mainExecutor: DelayableExecutor,
@Background private val bgExecutor: Executor,
- @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?,
- @KeyguardLargeClockLog private val largeLogBuffer: LogBuffer?,
+ private val clockBuffers: ClockMessageBuffers,
private val featureFlags: FeatureFlags,
private val zenModeController: ZenModeController,
) {
+ var loggers = listOf(
+ clockBuffers.infraMessageBuffer,
+ clockBuffers.smallClockMessageBuffer,
+ clockBuffers.largeClockMessageBuffer
+ ).map { Logger(it, TAG) }
+
var clock: ClockController? = null
+ get() = field
set(value) {
- smallClockOnAttachStateChangeListener?.let {
- field?.smallClock?.view?.removeOnAttachStateChangeListener(it)
+ disconnectClock(field)
+ field = value
+ connectClock(value)
+ }
+
+ private fun disconnectClock(clock: ClockController?) {
+ if (clock == null) { return; }
+ smallClockOnAttachStateChangeListener?.let {
+ clock.smallClock.view.removeOnAttachStateChangeListener(it)
+ smallClockFrame?.viewTreeObserver
+ ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
+ }
+ largeClockOnAttachStateChangeListener?.let {
+ clock.largeClock.view.removeOnAttachStateChangeListener(it)
+ }
+ }
+
+ private fun connectClock(clock: ClockController?) {
+ if (clock == null) { return; }
+ val clockStr = clock.toString()
+ loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } }
+
+ clock.initialize(resources, dozeAmount, 0f)
+
+ if (!regionSamplingEnabled) {
+ updateColors()
+ } else {
+ smallRegionSampler = createRegionSampler(
+ clock.smallClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ ).apply { startRegionSampler() }
+
+ largeRegionSampler = createRegionSampler(
+ clock.largeClock.view,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ isLockscreen = true,
+ ::updateColors
+ ).apply { startRegionSampler() }
+
+ updateColors()
+ }
+ updateFontSizes()
+ updateTimeListeners()
+
+ weatherData?.let {
+ if (WeatherData.DEBUG) {
+ Log.i(TAG, "Pushing cached weather data to new clock: $it")
+ }
+ clock.events.onWeatherDataChanged(it)
+ }
+ zenData?.let {
+ clock.events.onZenDataChanged(it)
+ }
+ alarmData?.let {
+ clock.events.onAlarmDataChanged(it)
+ }
+
+ smallClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
+ var pastVisibility: Int? = null
+ override fun onViewAttachedToWindow(view: View) {
+ clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ // Match the asing for view.parent's layout classes.
+ smallClockFrame = (view.parent as ViewGroup)?.also { frame ->
+ pastVisibility = frame.visibility
+ onGlobalLayoutListener = OnGlobalLayoutListener {
+ val currentVisibility = frame.visibility
+ if (pastVisibility != currentVisibility) {
+ pastVisibility = currentVisibility
+ // when small clock is visible,
+ // recalculate bounds and sample
+ if (currentVisibility == View.VISIBLE) {
+ smallRegionSampler?.stopRegionSampler()
+ smallRegionSampler?.startRegionSampler()
+ }
+ }
+ }
+ frame.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
+ }
+ }
+
+ override fun onViewDetachedFromWindow(p0: View) {
smallClockFrame?.viewTreeObserver
?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
}
- largeClockOnAttachStateChangeListener?.let {
- field?.largeClock?.view?.removeOnAttachStateChangeListener(it)
- }
-
- field = value
- if (value != null) {
- smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.smallClock.messageBuffer = smallLogBuffer
- largeLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
- value.largeClock.messageBuffer = largeLogBuffer
-
- value.initialize(resources, dozeAmount, 0f)
-
- if (!regionSamplingEnabled) {
- updateColors()
- } else {
- clock?.let {
- smallRegionSampler = createRegionSampler(
- it.smallClock.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- )?.apply { startRegionSampler() }
-
- largeRegionSampler = createRegionSampler(
- it.largeClock.view,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- isLockscreen = true,
- ::updateColors
- )?.apply { startRegionSampler() }
-
- updateColors()
- }
- }
- updateFontSizes()
- updateTimeListeners()
- weatherData?.let {
- if (WeatherData.DEBUG) {
- Log.i(TAG, "Pushing cached weather data to new clock: $it")
- }
- value.events.onWeatherDataChanged(it)
- }
- zenData?.let {
- value.events.onZenDataChanged(it)
- }
- alarmData?.let {
- value.events.onAlarmDataChanged(it)
- }
-
- smallClockOnAttachStateChangeListener =
- object : OnAttachStateChangeListener {
- var pastVisibility: Int? = null
- override fun onViewAttachedToWindow(view: View) {
- value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- // Match the asing for view.parent's layout classes.
- smallClockFrame = view.parent as ViewGroup
- smallClockFrame?.let { frame ->
- pastVisibility = frame.visibility
- onGlobalLayoutListener = OnGlobalLayoutListener {
- val currentVisibility = frame.visibility
- if (pastVisibility != currentVisibility) {
- pastVisibility = currentVisibility
- // when small clock is visible,
- // recalculate bounds and sample
- if (currentVisibility == View.VISIBLE) {
- smallRegionSampler?.stopRegionSampler()
- smallRegionSampler?.startRegionSampler()
- }
- }
- }
- frame.viewTreeObserver
- .addOnGlobalLayoutListener(onGlobalLayoutListener)
- }
- }
-
- override fun onViewDetachedFromWindow(p0: View) {
- smallClockFrame?.viewTreeObserver
- ?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
- }
- }
- value.smallClock.view
- .addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
-
- largeClockOnAttachStateChangeListener =
- object : OnAttachStateChangeListener {
- override fun onViewAttachedToWindow(p0: View) {
- value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- }
- override fun onViewDetachedFromWindow(p0: View) {
- }
- }
- value.largeClock.view
- .addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
- }
}
+ clock.smallClock.view.addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+
+ largeClockOnAttachStateChangeListener = object : OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(p0: View) {
+ clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+ override fun onViewDetachedFromWindow(p0: View) {}
+ }
+ clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
+ }
@VisibleForTesting
var smallClockOnAttachStateChangeListener: OnAttachStateChangeListener? = null
@@ -247,6 +249,7 @@
largeClock.events.onRegionDarknessChanged(isRegionDark)
}
}
+
protected open fun createRegionSampler(
sampledView: View,
mainExecutor: Executor?,
@@ -254,7 +257,7 @@
regionSamplingEnabled: Boolean,
isLockscreen: Boolean,
updateColors: () -> Unit
- ): RegionSampler? {
+ ): RegionSampler {
return RegionSampler(
sampledView,
mainExecutor,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index cdd7b80..74b975c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -39,7 +39,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
-import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -48,9 +47,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
-import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder;
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.KeyguardClockLog;
@@ -62,17 +59,11 @@
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-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.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.SecureSettings;
@@ -102,14 +93,7 @@
private final DumpManager mDumpManager;
private final ClockEventController mClockEventController;
private final LogBuffer mLogBuffer;
- private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel;
- private final KeyguardRootViewModel mKeyguardRootViewModel;
- private final ConfigurationState mConfigurationState;
- private final SystemBarUtilsState mSystemBarUtilsState;
- private final DozeParameters mDozeParameters;
- private final ScreenOffAnimationController mScreenOffAnimationController;
- private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
- private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
+ private final NotificationIconContainerAlwaysOnDisplayViewBinder mNicViewBinder;
private FrameLayout mSmallClockFrame; // top aligned clock
private FrameLayout mLargeClockFrame; // centered clock
@@ -183,9 +167,7 @@
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
LockscreenSmartspaceController smartspaceController,
- SystemBarUtilsState systemBarUtilsState,
- ScreenOffAnimationController screenOffAnimationController,
- StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
+ NotificationIconContainerAlwaysOnDisplayViewBinder nicViewBinder,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
@Main DelayableExecutor uiExecutor,
@@ -193,11 +175,6 @@
DumpManager dumpManager,
ClockEventController clockEventController,
@KeyguardClockLog LogBuffer logBuffer,
- NotificationIconContainerAlwaysOnDisplayViewModel aodIconsViewModel,
- KeyguardRootViewModel keyguardRootViewModel,
- ConfigurationState configurationState,
- DozeParameters dozeParameters,
- AlwaysOnDisplayNotificationIconViewStore aodIconViewStore,
KeyguardInteractor keyguardInteractor,
KeyguardClockInteractor keyguardClockInteractor,
FeatureFlagsClassic featureFlags,
@@ -208,9 +185,7 @@
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mSmartspaceController = smartspaceController;
- mSystemBarUtilsState = systemBarUtilsState;
- mScreenOffAnimationController = screenOffAnimationController;
- mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
+ mNicViewBinder = nicViewBinder;
mSecureSettings = secureSettings;
mUiExecutor = uiExecutor;
mBgExecutor = bgExecutor;
@@ -218,11 +193,6 @@
mDumpManager = dumpManager;
mClockEventController = clockEventController;
mLogBuffer = logBuffer;
- mAodIconsViewModel = aodIconsViewModel;
- mKeyguardRootViewModel = keyguardRootViewModel;
- mConfigurationState = configurationState;
- mDozeParameters = dozeParameters;
- mAodIconViewStore = aodIconViewStore;
mView.setLogBuffer(mLogBuffer);
mFeatureFlags = featureFlags;
mKeyguardInteractor = keyguardInteractor;
@@ -619,28 +589,7 @@
mAodIconsBindHandle.dispose();
}
if (nic != null) {
- final DisposableHandle viewHandle =
- NotificationIconContainerViewBinder.bindWhileAttached(
- nic,
- mAodIconsViewModel,
- mConfigurationState,
- mSystemBarUtilsState,
- mIconViewBindingFailureTracker,
- mAodIconViewStore);
- final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
- nic,
- mKeyguardRootViewModel.isNotifIconContainerVisible(),
- mConfigurationState,
- mFeatureFlags,
- mScreenOffAnimationController);
- if (visHandle == null) {
- mAodIconsBindHandle = viewHandle;
- } else {
- mAodIconsBindHandle = () -> {
- viewHandle.dispose();
- visHandle.dispose();
- };
- }
+ mAodIconsBindHandle = mNicViewBinder.bindWhileAttached(nic);
mAodIconContainer = nic;
}
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 714fe64..66f965a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -30,13 +30,13 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Flags;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.ui.BouncerMessageView;
import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.log.BouncerLogger;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -108,7 +108,7 @@
private void updateMessageAreaVisibility() {
if (mMessageAreaController == null) return;
- if (mFeatureFlags.isEnabled(Flags.REVAMPED_BOUNCER_MESSAGES)) {
+ if (Flags.revampedBouncerMessages()) {
mMessageAreaController.disable();
} else {
mMessageAreaController.setIsVisible(true);
@@ -182,15 +182,13 @@
public void bindMessageView(
@NonNull BouncerMessageInteractor bouncerMessageInteractor,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- BouncerLogger bouncerLogger,
- FeatureFlags featureFlags) {
+ BouncerLogger bouncerLogger) {
BouncerMessageView bouncerMessageView = (BouncerMessageView) mView.getBouncerMessageView();
if (bouncerMessageView != null) {
BouncerMessageViewBinder.bind(bouncerMessageView,
bouncerMessageInteractor,
messageAreaControllerFactory,
- bouncerLogger,
- featureFlags);
+ bouncerLogger);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index cce2018..5e35e77 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -31,7 +31,6 @@
import static com.android.keyguard.KeyguardSecurityModel.SecurityMode.SimPuk;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
@@ -1164,7 +1163,7 @@
mLockPatternUtils.reportFailedPasswordAttempt(userId);
if (timeoutMs > 0) {
mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
- if (!mFeatureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) {
+ if (!com.android.systemui.Flags.revampedBouncerMessages()) {
mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
mSecurityModel.getSecurityMode(userId));
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9c61a8a..f3cd01f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -111,6 +111,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -175,7 +176,7 @@
* to be updated.
*/
@SysUISingleton
-public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable {
+public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable, CoreStartable {
private static final String TAG = "KeyguardUpdateMonitor";
private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
@@ -347,6 +348,7 @@
private final LatencyTracker mLatencyTracker;
private final StatusBarStateController mStatusBarStateController;
private final Executor mBackgroundExecutor;
+ private final Executor mMainExecutor;
private final SensorPrivacyManager mSensorPrivacyManager;
private final ActiveUnlockConfig mActiveUnlockConfig;
private final IDreamManager mDreamManager;
@@ -354,7 +356,10 @@
@Nullable
private final FingerprintManager mFpm;
@Nullable
+ private final BiometricManager mBiometricManager;
+ @Nullable
private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+ private final DevicePostureController mDevicePostureController;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final IActivityTaskManager mActivityTaskManager;
private final SelectedUserInteractor mSelectedUserInteractor;
@@ -370,7 +375,7 @@
private boolean mIsDreaming;
private boolean mLogoutEnabled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
+ private final FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
/**
* Short delay before restarting fingerprint authentication after a successful try. This should
@@ -2105,6 +2110,7 @@
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
mStrongAuthTracker = new StrongAuthTracker(context);
mBackgroundExecutor = backgroundExecutor;
+ mMainExecutor = mainExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mInteractionJankMonitor = interactionJankMonitor;
mLatencyTracker = latencyTracker;
@@ -2126,7 +2132,7 @@
mDevicePolicyManager = devicePolicyManager;
mPackageManager = packageManager;
mFpm = fingerprintManager;
- mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
+ mBiometricManager = biometricManager;
mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
R.integer.config_face_auth_supported_posture);
mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
@@ -2134,10 +2140,14 @@
mContext.getResources().getStringArray(
R.array.config_fingerprint_listen_on_occluding_activity_packages))
.collect(Collectors.toSet());
+ mDevicePostureController = devicePostureController;
mTaskStackChangeListeners = taskStackChangeListeners;
mActivityTaskManager = activityTaskManagerService;
mSelectedUserInteractor = selectedUserInteractor;
+ mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
+ mIsSystemUser = mUserManager.isSystemUser();
+
mHandler = new Handler(mainLooper) {
@Override
public void handleMessage(Message msg) {
@@ -2252,6 +2262,20 @@
}
};
+ mTimeFormatChangeObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_TIME_FORMAT_UPDATE,
+ Settings.System.getString(
+ mContext.getContentResolver(),
+ Settings.System.TIME_12_24)));
+ }
+ };
+ }
+
+ @Override
+ public void start() {
// Since device can't be un-provisioned, we only need to register a content observer
// to update mDeviceProvisioned when we are...
if (!mDeviceProvisioned) {
@@ -2297,7 +2321,7 @@
mHandler, UserHandle.ALL);
mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
- mUserTracker.addCallback(mUserChangedCallback, mainExecutor);
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
mTrustManager.registerTrustListener(this);
@@ -2318,8 +2342,8 @@
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
}
- if (biometricManager != null) {
- biometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
+ if (mBiometricManager != null) {
+ mBiometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
}
// in case authenticators aren't registered yet at this point:
@@ -2327,7 +2351,7 @@
@Override
public void onAllAuthenticatorsRegistered(
@BiometricAuthenticator.Modality int modality) {
- mainExecutor.execute(
+ mMainExecutor.execute(
() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
}
@@ -2335,7 +2359,7 @@
public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
mHandler.obtainMessage(MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED, modality, 0)
.sendToTarget();
- mainExecutor.execute(
+ mMainExecutor.execute(
() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
}
@@ -2353,12 +2377,11 @@
}
});
if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
- devicePostureController.addCallback(mPostureCallback);
+ mDevicePostureController.addCallback(mPostureCallback);
}
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
- mIsSystemUser = mUserManager.isSystemUser();
int user = mSelectedUserInteractor.getSelectedUserId(true);
mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
@@ -2377,22 +2400,9 @@
mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener);
initializeSimState();
- mTimeFormatChangeObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- mHandler.sendMessage(mHandler.obtainMessage(
- MSG_TIME_FORMAT_UPDATE,
- Settings.System.getString(
- mContext.getContentResolver(),
- Settings.System.TIME_12_24)));
- }
- };
-
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TIME_12_24),
false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
-
- mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
}
private void initializeSimState() {
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 661ce2c..878a5d8 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -28,9 +28,8 @@
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.dagger.KeyguardClockLog;
import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.shared.clocks.DefaultClockProvider;
@@ -56,7 +55,7 @@
FeatureFlags featureFlags,
@Main Resources resources,
LayoutInflater layoutInflater,
- @KeyguardClockLog LogBuffer logBuffer) {
+ ClockMessageBuffers clockBuffers) {
ClockRegistry registry = new ClockRegistry(
context,
pluginManager,
@@ -72,7 +71,7 @@
featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION),
migrateClocksToBlueprint()),
context.getString(R.string.lockscreen_clock_id_fallback),
- logBuffer,
+ clockBuffers,
/* keepAllLoaded = */ false,
/* subTag = */ "System",
/* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK));
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index c07a4d2..4c9782c 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -16,8 +16,6 @@
package com.android.systemui;
-import android.content.res.Configuration;
-
import androidx.annotation.NonNull;
import java.io.PrintWriter;
@@ -35,6 +33,9 @@
* abstract fun bind(impl: FoobarStartable): CoreStartable
* </pre>
*
+ * If your CoreStartable depends on different CoreStartables starting before it, use a
+ * {@link com.android.systemui.startable.Dependencies} annotation to list out those dependencies.
+ *
* @see SystemUIApplication#startServicesIfNeeded()
*/
public interface CoreStartable extends Dumpable {
@@ -42,13 +43,6 @@
/** Main entry point for implementations. Called shortly after SysUI startup. */
void start();
- /** Called when the device configuration changes. This will not be called before
- * {@link #start()}, but it could be called before {@link #onBootCompleted()}.
- *
- * @see android.app.Application#onConfigurationChanged(Configuration) */
- default void onConfigurationChanged(Configuration newConfig) {
- }
-
@Override
default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 008de43..e03c627 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -89,6 +89,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.SecureSettings;
@@ -109,7 +110,8 @@
* for antialiasing and emulation purposes.
*/
@SysUISingleton
-public class ScreenDecorations implements CoreStartable, Dumpable {
+public class ScreenDecorations implements
+ CoreStartable, ConfigurationController.ConfigurationListener, Dumpable {
private static final boolean DEBUG_LOGGING = false;
private static final String TAG = "ScreenDecorations";
@@ -575,7 +577,7 @@
if (mPendingManualConfigUpdate) {
mPendingManualConfigUpdate = false;
- onConfigurationChanged(mContext.getResources().getConfiguration());
+ onConfigChanged(mContext.getResources().getConfiguration());
}
}
}
@@ -1062,7 +1064,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
Log.i(TAG, "ScreenDecorations is disabled");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
new file mode 100644
index 0000000..044312b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
@@ -0,0 +1,38 @@
+/*
+ * 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
+
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface ScreenDecorationsModule {
+ /** Start ScreenDecorations. */
+ @Binds
+ @IntoMap
+ @ClassKey(ScreenDecorations::class)
+ fun bindScreenDecorationsCoreStartable(impl: ScreenDecorations): CoreStartable
+
+ /** Listen to config changes for ScreenDecorations. */
+ @Binds
+ @IntoSet
+ fun bindScreenDecorationsConfigListener(impl: ScreenDecorations): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index c3f6480..8aae206 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.annotation.SuppressLint;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Notification;
@@ -26,27 +27,33 @@
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.os.Looper;
import android.os.Process;
-import android.os.SystemProperties;
import android.os.Trace;
-import android.os.UserHandle;
import android.util.Log;
import android.util.TimingsTraceLog;
import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
import android.view.View;
-import com.android.systemui.res.R;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.protolog.common.ProtoLog;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.res.R;
+import com.android.systemui.startable.Dependencies;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.NotificationChannels;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayDeque;
+import java.util.Arrays;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.Map;
+import java.util.StringJoiner;
import java.util.TreeMap;
import javax.inject.Provider;
@@ -78,10 +85,17 @@
ProtoLog.REQUIRE_PROTOLOGTOOL = false;
}
+ @VisibleForTesting
+ @Override
+ public void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ }
+
protected GlobalRootComponent getRootComponent() {
return mInitializer.getRootComponent();
}
+ @SuppressLint("RegisterReceiverViaContext")
@Override
public void onCreate() {
super.onCreate();
@@ -96,9 +110,11 @@
mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
log.traceEnd();
+ GlobalRootComponent rootComponent = mInitializer.getRootComponent();
+
// Enable Looper trace points.
// This allows us to see Handler callbacks on traces.
- Looper.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
+ rootComponent.getMainLooper().setTraceTag(Trace.TRACE_TAG_APP);
// Set the application theme that is inherited by all services. Note that setting the
// application theme in the manifest does only work for activities. Keep this in sync with
@@ -106,15 +122,17 @@
setTheme(R.style.Theme_SystemUI);
View.setTraceLayoutSteps(
- SystemProperties.getBoolean("persist.debug.trace_layouts", false));
+ rootComponent.getSystemPropertiesHelper()
+ .getBoolean("persist.debug.trace_layouts", false));
View.setTracedRequestLayoutClassClass(
- SystemProperties.get("persist.debug.trace_request_layout_class", null));
+ rootComponent.getSystemPropertiesHelper()
+ .get("persist.debug.trace_request_layout_class", null));
if (Flags.enableLayoutTracing()) {
View.setTraceLayoutSteps(true);
}
- if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
+ if (rootComponent.getProcessWrapper().isSystemUser()) {
IntentFilter bootCompletedFilter = new
IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -175,8 +193,8 @@
}
/**
- * Makes sure that all the SystemUI services are running. If they are already running, this is a
- * no-op. This is needed to conditinally start all the services, as we only need to have it in
+ * Makes sure that all the CoreStartables are running. If they are already running, this is a
+ * no-op. This is needed to conditionally start all the services, as we only need to have it in
* the main process.
* <p>This method must only be called from the main thread.</p>
*/
@@ -221,7 +239,8 @@
if (!mBootCompleteCache.isBootComplete()) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
- if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
+ if ("1".equals(getRootComponent().getSystemPropertiesHelper()
+ .get("sys.boot_completed"))) {
mBootCompleteCache.setBootComplete();
if (DEBUG) {
Log.v(TAG, "BOOT_COMPLETED was already sent");
@@ -237,17 +256,78 @@
Trace.TRACE_TAG_APP);
log.traceBegin(metricsPrefix);
- int i = 0;
- for (Map.Entry<Class<?>, Provider<CoreStartable>> entry : startables.entrySet()) {
- String clsName = entry.getKey().getName();
- int j = i; // Copied to make lambda happy.
- timeInitialization(
- clsName,
- () -> mServices[j] = startStartable(clsName, entry.getValue()),
- log,
- metricsPrefix);
- i++;
+ HashSet<Class<?>> startedStartables = new HashSet<>();
+
+ // Perform a form of topological sort:
+ // 1) Iterate through a queue of all non-started startables
+ // If the startable has all of its dependencies met
+ // - start it
+ // Else
+ // - enqueue it for the next iteration
+ // 2) If anything was started and the "next" queue is not empty, loop back to 1
+ // 3) If we're done looping and there are any non-started startables left, throw an error.
+ //
+ // This "sort" is not very optimized. We assume that most CoreStartables don't have many
+ // dependencies - zero in fact. We assume two or three iterations of this loop will be
+ // enough. If that ever changes, it may be worth revisiting.
+
+ log.traceBegin("Topologically start Core Startables");
+ boolean startedAny = false;
+ ArrayDeque<Map.Entry<Class<?>, Provider<CoreStartable>>> queue;
+ ArrayDeque<Map.Entry<Class<?>, Provider<CoreStartable>>> nextQueue =
+ new ArrayDeque<>(startables.entrySet());
+ int numIterations = 0;
+
+ int serviceIndex = 0;
+
+ do {
+ queue = nextQueue;
+ nextQueue = new ArrayDeque<>(startables.size());
+
+ while (!queue.isEmpty()) {
+ Map.Entry<Class<?>, Provider<CoreStartable>> entry = queue.removeFirst();
+
+ Class<?> cls = entry.getKey();
+ Dependencies dep = cls.getAnnotation(Dependencies.class);
+ Class<? extends CoreStartable>[] deps = (dep == null ? null : dep.value());
+ if (deps == null || startedStartables.containsAll(Arrays.asList(deps))) {
+ String clsName = cls.getName();
+ int i = serviceIndex; // Copied to make lambda happy.
+ timeInitialization(
+ clsName,
+ () -> mServices[i] = startStartable(clsName, entry.getValue()),
+ log,
+ metricsPrefix);
+ startedStartables.add(cls);
+ startedAny = true;
+ serviceIndex++;
+ } else {
+ nextQueue.add(entry);
+ }
+ }
+ numIterations++;
+ } while (startedAny && !nextQueue.isEmpty()); // if none were started, stop.
+
+ if (!nextQueue.isEmpty()) { // If some startables were left over, throw an error.
+ while (!nextQueue.isEmpty()) {
+ Map.Entry<Class<?>, Provider<CoreStartable>> entry = nextQueue.removeFirst();
+ Class<?> cls = entry.getKey();
+ Dependencies dep = cls.getAnnotation(Dependencies.class);
+ Class<? extends CoreStartable>[] deps = (dep == null ? null : dep.value());
+ StringJoiner stringJoiner = new StringJoiner(", ");
+ for (int i = 0; deps != null && i < deps.length; i++) {
+ if (!startedStartables.contains(deps[i])) {
+ stringJoiner.add(deps[i].getName());
+ }
+ }
+ Log.e(TAG, "Failed to start " + cls.getName()
+ + ". Missing dependencies: [" + stringJoiner + "]");
+ }
+
+ throw new RuntimeException("Failed to start all CoreStartables. Check logcat!");
}
+ Log.i(TAG, "Topological CoreStartables completed in " + numIterations + " iterations");
+ log.traceEnd();
if (vendorComponent != null) {
timeInitialization(
@@ -258,8 +338,8 @@
metricsPrefix);
}
- for (i = 0; i < mServices.length; i++) {
- final CoreStartable service = mServices[i];
+ for (serviceIndex = 0; serviceIndex < mServices.length; serviceIndex++) {
+ final CoreStartable service = mServices[serviceIndex];
if (mBootCompleteCache.isBootComplete()) {
notifyBootCompleted(service);
}
@@ -308,10 +388,14 @@
Trace.TRACE_TAG_APP, clsName + ".newInstance()");
}
try {
- startable = (CoreStartable) Class.forName(clsName).newInstance();
+ startable = (CoreStartable) Class.forName(clsName)
+ .getDeclaredConstructor()
+ .newInstance();
} catch (ClassNotFoundException
- | IllegalAccessException
- | InstantiationException ex) {
+ | IllegalAccessException
+ | InstantiationException
+ | NoSuchMethodException
+ | InvocationTargetException ex) {
throw new RuntimeException(ex);
} finally {
Trace.endSection();
@@ -344,7 +428,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
if (mServicesStarted) {
ConfigurationController configController = mSysUIComponent.getConfigurationController();
if (Trace.isEnabled()) {
@@ -354,19 +438,6 @@
}
configController.onConfigurationChanged(newConfig);
Trace.endSection();
- int len = mServices.length;
- for (int i = 0; i < len; i++) {
- if (mServices[i] != null) {
- if (Trace.isEnabled()) {
- Trace.traceBegin(
- Trace.TRACE_TAG_APP,
- mServices[i].getClass().getSimpleName()
- + ".onConfigurationChanged()");
- }
- mServices[i].onConfigurationChanged(newConfig);
- Trace.endSection();
- }
- }
}
}
@@ -376,7 +447,7 @@
@Override
public void setContextAvailableCallback(
- SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) {
+ @NonNull SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) {
mContextAvailableCallback = callback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 3cb6314..3ca95e1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -77,7 +77,7 @@
@VisibleForTesting
SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
- private static class ControllerSupplier extends
+ private static class WindowMagnificationControllerSupplier extends
DisplayIdIndexSupplier<WindowMagnificationController> {
private final Context mContext;
@@ -86,7 +86,7 @@
private final SysUiState mSysUiState;
private final SecureSettings mSecureSettings;
- ControllerSupplier(Context context, Handler handler,
+ WindowMagnificationControllerSupplier(Context context, Handler handler,
WindowMagnifierCallback windowMagnifierCallback,
DisplayManager displayManager, SysUiState sysUiState,
SecureSettings secureSettings) {
@@ -118,7 +118,7 @@
}
@VisibleForTesting
- DisplayIdIndexSupplier<WindowMagnificationController> mMagnificationControllerSupplier;
+ DisplayIdIndexSupplier<WindowMagnificationController> mWindowMagnificationControllerSupplier;
private static class SettingsSupplier extends
DisplayIdIndexSupplier<MagnificationSettingsController> {
@@ -168,7 +168,7 @@
mOverviewProxyService = overviewProxyService;
mDisplayTracker = displayTracker;
mA11yLogger = a11yLogger;
- mMagnificationControllerSupplier = new ControllerSupplier(context,
+ mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
mHandler, mWindowMagnifierCallback,
displayManager, sysUiState, secureSettings);
mMagnificationSettingsSupplier = new SettingsSupplier(context,
@@ -196,7 +196,8 @@
private void updateSysUiStateFlag() {
//TODO(b/187510533): support multi-display once SysuiState supports it.
final WindowMagnificationController controller =
- mMagnificationControllerSupplier.valueAt(mDisplayTracker.getDefaultDisplayId());
+ mWindowMagnificationControllerSupplier.valueAt(
+ mDisplayTracker.getDefaultDisplayId());
if (controller != null) {
controller.updateSysUIStateFlag();
} else {
@@ -212,7 +213,7 @@
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
@@ -222,7 +223,7 @@
@MainThread
void setScaleForWindowMagnification(int displayId, float scale) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.setScale(scale);
}
@@ -231,7 +232,7 @@
@MainThread
void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
final WindowMagnificationController windowMagnificationcontroller =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationcontroller != null) {
windowMagnificationcontroller.moveWindowMagnifier(offsetX, offsetY);
}
@@ -241,7 +242,7 @@
void moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY,
IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
callback);
@@ -252,7 +253,7 @@
void disableWindowMagnification(int displayId,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.deleteWindowMagnification(callback);
}
@@ -417,7 +418,7 @@
@MainThread
private void onSetMagnifierSizeInternal(int displayId, int index) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.changeMagnificationSize(index);
}
@@ -426,7 +427,7 @@
@MainThread
private void onSetDiagonalScrollingInternal(int displayId, boolean enable) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.setDiagonalScrolling(enable);
}
@@ -435,7 +436,7 @@
@MainThread
private void onEditMagnifierSizeModeInternal(int displayId, boolean enable) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
windowMagnificationController.setEditMagnifierSizeMode(enable);
}
@@ -444,7 +445,7 @@
@MainThread
private void onModeSwitchInternal(int displayId, int newMode) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
final boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
final boolean isSwitchToWindowMode = (newMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
final boolean changed = isSwitchToWindowMode ^ isWindowMagnifierActivated;
@@ -463,7 +464,7 @@
@MainThread
private void onSettingsPanelVisibilityChangedInternal(int displayId, boolean shown) {
final WindowMagnificationController windowMagnificationController =
- mMagnificationControllerSupplier.get(displayId);
+ mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
if (isWindowMagnifierActivated) {
@@ -495,7 +496,7 @@
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println(TAG);
- mMagnificationControllerSupplier.forEach(
+ mWindowMagnificationControllerSupplier.forEach(
magnificationController -> magnificationController.dump(pw));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 7a8161e..da49201 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
+
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import android.accessibilityservice.AccessibilityService;
@@ -57,6 +58,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
@@ -71,7 +73,7 @@
* Class to register system actions with accessibility framework.
*/
@SysUISingleton
-public class SystemActions implements CoreStartable {
+public class SystemActions implements CoreStartable, ConfigurationController.ConfigurationListener {
private static final String TAG = "SystemActions";
/**
@@ -234,7 +236,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
final Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
if (!locale.equals(mLocale)) {
mLocale = locale;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActionsModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActionsModule.kt
new file mode 100644
index 0000000..4d6d784
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActionsModule.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.accessibility
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface SystemActionsModule {
+ /** Start SystemActions. */
+ @Binds
+ @IntoMap
+ @ClassKey(SystemActions::class)
+ fun bindSystemActionsStartable(sysui: SystemActions): CoreStartable
+
+ /** Listen to config changes for SystemActions. */
+ @Binds @IntoSet fun bindSystemActionsConfigChanges(sysui: SystemActions): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0bd4859..dde9f48 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1303,7 +1303,7 @@
} else if (id == R.id.close_button) {
setEditMagnifierSizeMode(false);
} else {
- animateBounceEffect();
+ animateBounceEffectIfNeeded();
}
}
@@ -1465,7 +1465,12 @@
mBounceEffectDuration = duration;
}
- private void animateBounceEffect() {
+ private void animateBounceEffectIfNeeded() {
+ if (mMirrorView == null) {
+ // run the animation only if the mirror view is not null
+ return;
+ }
+
final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mMirrorView,
PropertyValuesHolder.ofFloat(View.SCALE_X, 1, mBounceEffectAnimationScale, 1),
PropertyValuesHolder.ofFloat(View.SCALE_Y, 1, mBounceEffectAnimationScale, 1));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
rename to packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index b1de127..49e0df6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -31,7 +31,7 @@
* Controls the interaction between {@link MagnetizedObject} and
* {@link MagnetizedObject.MagneticTarget}.
*/
-class DismissAnimationController {
+class DragToInteractAnimationController {
private static final boolean ENABLE_FLING_TO_DISMISS_MENU = false;
private static final float COMPLETELY_OPAQUE = 1.0f;
private static final float COMPLETELY_TRANSPARENT = 0.0f;
@@ -45,7 +45,7 @@
private float mMinDismissSize;
private float mSizePercent;
- DismissAnimationController(DismissView dismissView, MenuView menuView) {
+ DragToInteractAnimationController(DismissView dismissView, MenuView menuView) {
mDismissView = dismissView;
mDismissView.setPivotX(dismissView.getWidth() / 2.0f);
mDismissView.setPivotY(dismissView.getHeight() / 2.0f);
@@ -127,7 +127,7 @@
* @param event that move the magnetized object which is also the menu list view.
* @return true if the location of the motion events moves within the magnetic field of a
* target, but false if didn't set
- * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
+ * {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
*/
boolean maybeConsumeMoveMotionEvent(MotionEvent event) {
return mMagnetizedObject.maybeConsumeMotionEvent(event);
@@ -140,7 +140,7 @@
* @param event that move the magnetized object which is also the menu list view.
* @return true if the location of the motion events moves within the magnetic field of a
* target, but false if didn't set
- * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
+ * {@link DragToInteractAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}.
*/
boolean maybeConsumeUpMotionEvent(MotionEvent event) {
return mMagnetizedObject.maybeConsumeMotionEvent(event);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 34d7cec..a270558 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -73,7 +73,7 @@
private final ValueAnimator mFadeOutAnimator;
private final Handler mHandler;
private boolean mIsFadeEffectEnabled;
- private DismissAnimationController.DismissCallback mDismissCallback;
+ private DragToInteractAnimationController.DismissCallback mDismissCallback;
private Runnable mSpringAnimationsEndAction;
// Cache the animations state of {@link DynamicAnimation.TRANSLATION_X} and {@link
@@ -171,7 +171,7 @@
}
void setDismissCallback(
- DismissAnimationController.DismissCallback dismissCallback) {
+ DragToInteractAnimationController.DismissCallback dismissCallback) {
mDismissCallback = dismissCallback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
index d01590f..52e7b91 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
@@ -40,13 +40,13 @@
private final PointF mMenuTranslationDown = new PointF();
private boolean mIsDragging = false;
private float mTouchSlop;
- private final DismissAnimationController mDismissAnimationController;
+ private final DragToInteractAnimationController mDragToInteractAnimationController;
private Optional<Runnable> mOnActionDownEnd = Optional.empty();
MenuListViewTouchHandler(MenuAnimationController menuAnimationController,
- DismissAnimationController dismissAnimationController) {
+ DragToInteractAnimationController dragToInteractAnimationController) {
mMenuAnimationController = menuAnimationController;
- mDismissAnimationController = dismissAnimationController;
+ mDragToInteractAnimationController = dragToInteractAnimationController;
}
@Override
@@ -67,7 +67,7 @@
mMenuTranslationDown.set(menuView.getTranslationX(), menuView.getTranslationY());
mMenuAnimationController.cancelAnimations();
- mDismissAnimationController.maybeConsumeDownMotionEvent(motionEvent);
+ mDragToInteractAnimationController.maybeConsumeDownMotionEvent(motionEvent);
mOnActionDownEnd.ifPresent(Runnable::run);
break;
@@ -78,9 +78,10 @@
mMenuAnimationController.onDraggingStart();
}
- mDismissAnimationController.showDismissView(/* show= */ true);
+ mDragToInteractAnimationController.showDismissView(/* show= */ true);
- if (!mDismissAnimationController.maybeConsumeMoveMotionEvent(motionEvent)) {
+ if (!mDragToInteractAnimationController.maybeConsumeMoveMotionEvent(
+ motionEvent)) {
mMenuAnimationController.moveToPositionX(mMenuTranslationDown.x + dx);
mMenuAnimationController.moveToPositionYIfNeeded(
mMenuTranslationDown.y + dy);
@@ -94,17 +95,18 @@
mIsDragging = false;
if (mMenuAnimationController.maybeMoveToEdgeAndHide(endX)) {
- mDismissAnimationController.showDismissView(/* show= */ false);
+ mDragToInteractAnimationController.showDismissView(/* show= */ false);
mMenuAnimationController.fadeOutIfEnabled();
return true;
}
- if (!mDismissAnimationController.maybeConsumeUpMotionEvent(motionEvent)) {
+ if (!mDragToInteractAnimationController.maybeConsumeUpMotionEvent(
+ motionEvent)) {
mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS);
mMenuAnimationController.flingMenuThenSpringToEdge(endX,
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
- mDismissAnimationController.showDismissView(/* show= */ false);
+ mDragToInteractAnimationController.showDismissView(/* show= */ false);
}
// Avoid triggering the listener of the item.
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 ff3a9e3..62d5feb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -94,7 +94,7 @@
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final IAccessibilityFloatingMenu mFloatingMenu;
private final SecureSettings mSecureSettings;
- private final DismissAnimationController mDismissAnimationController;
+ private final DragToInteractAnimationController mDragToInteractAnimationController;
private final MenuViewModel mMenuViewModel;
private final Observer<Boolean> mDockTooltipObserver =
this::onDockTooltipVisibilityChanged;
@@ -188,29 +188,30 @@
mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction);
mDismissView = new DismissView(context);
DismissViewUtils.setup(mDismissView);
- mDismissAnimationController = new DismissAnimationController(mDismissView, mMenuView);
- mDismissAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mDismissView, mMenuView);
+ mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
@Override
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mDismissAnimationController.animateDismissMenu(/* scaleUp= */ true);
+ mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ true);
}
@Override
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
float velocityX, float velocityY, boolean wasFlungOut) {
- mDismissAnimationController.animateDismissMenu(/* scaleUp= */ false);
+ mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
}
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
hideMenuAndShowMessage();
mDismissView.hide();
- mDismissAnimationController.animateDismissMenu(/* scaleUp= */ false);
+ mDragToInteractAnimationController.animateDismissMenu(/* scaleUp= */ false);
}
});
mMenuListViewTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
- mDismissAnimationController);
+ mDragToInteractAnimationController);
mMenuView.addOnItemTouchListenerToList(mMenuListViewTouchHandler);
mMenuView.setMoveToTuckedListener(this);
@@ -243,7 +244,7 @@
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
mDismissView.updateResources();
- mDismissAnimationController.updateResources();
+ mDragToInteractAnimationController.updateResources();
}
@Override
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 fda23b7f..49f34f1 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
@@ -18,19 +18,25 @@
package com.android.systemui.authentication.data.repository
+import android.annotation.UserIdInt
import android.app.admin.DevicePolicyManager
import android.content.IntentFilter
import android.os.UserHandle
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+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.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.SceneContainerFlags
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
@@ -43,9 +49,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -60,12 +64,6 @@
/** Defines interface for classes that can access authentication-related application state. */
interface AuthenticationRepository {
/**
- * Emits the result whenever a PIN/Pattern/Password security challenge is attempted by the user
- * in order to unlock the device.
- */
- val authenticationChallengeResult: SharedFlow<Boolean>
-
- /**
* The exact length a PIN should be for us to enable PIN length hinting.
*
* A PIN that's shorter or longer than this is not eligible for the UI to render hints showing
@@ -80,16 +78,6 @@
val isPatternVisible: StateFlow<Boolean>
/**
- * The current authentication lockout (aka "throttling") state, set when the user has to wait
- * before being able to try another authentication attempt. `null` indicates throttling isn't
- * active.
- */
- val lockout: MutableStateFlow<AuthenticationLockoutModel?>
-
- /** Whether throttling has occurred at least once since the last successful authentication. */
- val hasLockoutOccurred: MutableStateFlow<Boolean>
-
- /**
* Whether the auto confirm feature is enabled for the currently-selected user.
*
* Note that the length of the PIN is also important to take into consideration, please see
@@ -98,6 +86,28 @@
val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
/**
+ * The number of failed authentication attempts for the selected user since their last
+ * successful authentication.
+ */
+ val failedAuthenticationAttempts: StateFlow<Int>
+
+ /**
+ * Timestamp for when the current lockout (aka "throttling") will end, allowing the user to
+ * attempt authentication again. Returns `null` if no lockout is active.
+ *
+ * Note that the value is in milliseconds and matches [SystemClock.elapsedRealtime].
+ *
+ * Also note that the value may change when the selected user is changed.
+ */
+ val lockoutEndTimestamp: Long?
+
+ /**
+ * Whether lockout has occurred at least once since the last successful authentication of any
+ * user.
+ */
+ val hasLockoutOccurred: StateFlow<Boolean>
+
+ /**
* The currently-configured authentication method. This determines how the authentication
* challenge needs to be completed in order to unlock an otherwise locked device.
*
@@ -120,6 +130,12 @@
val isPinEnhancedPrivacyEnabled: StateFlow<Boolean>
/**
+ * Checks the given [LockscreenCredential] to see if it's correct, returning an
+ * [AuthenticationResultModel] representing what happened.
+ */
+ suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel
+
+ /**
* Returns the currently-configured authentication method. This determines how the
* authentication challenge needs to be completed in order to unlock an otherwise locked device.
*
@@ -142,28 +158,27 @@
/** Reports that the user has entered a temporary device lockout (throttling). */
suspend fun reportLockoutStarted(durationMs: Int)
- /** Returns the current number of failed authentication attempts. */
- suspend fun getFailedAuthenticationAttemptCount(): Int
-
/**
- * Returns the timestamp for when the current lockout will end, allowing the user to attempt
- * authentication again.
+ * Returns the current maximum number of login attempts that are allowed before the device or
+ * profile is wiped.
*
- * Note that this is in milliseconds and it matches [SystemClock.elapsedRealtime].
+ * If there is no wipe policy, returns `0`.
+ *
+ * @see [DevicePolicyManager.getMaximumFailedPasswordsForWipe]
*/
- suspend fun getLockoutEndTimestamp(): Long
+ suspend fun getMaxFailedUnlockAttemptsForWipe(): Int
/**
- * Sets the lockout timeout duration (time during which the user should not be allowed to
- * attempt authentication).
+ * Returns the user that will be wiped first when too many failed attempts are made to unlock
+ * the device by the selected user. That user is either the same as the current user ID or
+ * belongs to the same profile group.
+ *
+ * When there is no such policy, returns [UserHandle.USER_NULL]. E.g. managed profile user may
+ * be wiped as a result of failed primary profile password attempts when using unified
+ * challenge. Primary user may be wiped as a result of failed password attempts on the managed
+ * profile of an organization-owned device.
*/
- suspend fun setLockoutDuration(durationMs: Int)
-
- /**
- * Checks the given [LockscreenCredential] to see if it's correct, returning an
- * [AuthenticationResultModel] representing what happened.
- */
- suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel
+ @UserIdInt suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int
}
@SysUISingleton
@@ -172,15 +187,16 @@
constructor(
@Application private val applicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ flags: SceneContainerFlags,
+ private val clock: SystemClock,
private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
private val userRepository: UserRepository,
private val lockPatternUtils: LockPatternUtils,
+ private val devicePolicyManager: DevicePolicyManager,
broadcastDispatcher: BroadcastDispatcher,
mobileConnectionsRepository: MobileConnectionsRepository,
) : AuthenticationRepository {
- override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
-
override val hintedPinLength: Int = 6
override val isPatternVisible: StateFlow<Boolean> =
@@ -189,10 +205,6 @@
getFreshValue = lockPatternUtils::isVisiblePatternEnabled,
)
- override val lockout: MutableStateFlow<AuthenticationLockoutModel?> = MutableStateFlow(null)
-
- override val hasLockoutOccurred: MutableStateFlow<Boolean> = MutableStateFlow(false)
-
override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
refreshingFlow(
initialValue = false,
@@ -217,11 +229,7 @@
.onStart { emit(Unit) }
.map { selectedUserId }
}
- .map { selectedUserId ->
- withContext(backgroundDispatcher) {
- blockingAuthenticationMethodInternal(selectedUserId)
- }
- }
+ .map(::getAuthenticationMethod)
.distinctUntilChanged()
override val minPatternLength: Int = LockPatternUtils.MIN_LOCK_PATTERN_SIZE
@@ -234,48 +242,28 @@
getFreshValue = { userId -> lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) },
)
- override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
- return withContext(backgroundDispatcher) {
- blockingAuthenticationMethodInternal(selectedUserId)
- }
- }
+ private val _failedAuthenticationAttempts = MutableStateFlow(0)
+ override val failedAuthenticationAttempts: StateFlow<Int> =
+ _failedAuthenticationAttempts.asStateFlow()
- override suspend fun getPinLength(): Int {
- return withContext(backgroundDispatcher) { lockPatternUtils.getPinLength(selectedUserId) }
- }
-
- override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
- withContext(backgroundDispatcher) {
- if (isSuccessful) {
- lockPatternUtils.reportSuccessfulPasswordAttempt(selectedUserId)
- } else {
- lockPatternUtils.reportFailedPasswordAttempt(selectedUserId)
+ override val lockoutEndTimestamp: Long?
+ get() =
+ lockPatternUtils.getLockoutAttemptDeadline(selectedUserId).takeIf {
+ clock.elapsedRealtime() < it
}
- authenticationChallengeResult.emit(isSuccessful)
- }
- }
- override suspend fun reportLockoutStarted(durationMs: Int) {
- return withContext(backgroundDispatcher) {
- lockPatternUtils.reportPasswordLockout(durationMs, selectedUserId)
- }
- }
+ private val _hasLockoutOccurred = MutableStateFlow(false)
+ override val hasLockoutOccurred: StateFlow<Boolean> = _hasLockoutOccurred.asStateFlow()
- override suspend fun getFailedAuthenticationAttemptCount(): Int {
- return withContext(backgroundDispatcher) {
- lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId)
- }
- }
-
- override suspend fun getLockoutEndTimestamp(): Long {
- return withContext(backgroundDispatcher) {
- lockPatternUtils.getLockoutAttemptDeadline(selectedUserId)
- }
- }
-
- override suspend fun setLockoutDuration(durationMs: Int) {
- withContext(backgroundDispatcher) {
- lockPatternUtils.setLockoutAttemptDeadline(selectedUserId, durationMs)
+ init {
+ if (flags.isEnabled()) {
+ // Hydrate failedAuthenticationAttempts initially and whenever the selected user
+ // changes.
+ applicationScope.launch {
+ userRepository.selectedUserInfo.collect {
+ _failedAuthenticationAttempts.value = getFailedAuthenticationAttemptCount()
+ }
+ }
}
}
@@ -292,8 +280,53 @@
}
}
+ override suspend fun getAuthenticationMethod(): AuthenticationMethodModel =
+ getAuthenticationMethod(selectedUserId)
+
+ override suspend fun getPinLength(): Int {
+ return withContext(backgroundDispatcher) { lockPatternUtils.getPinLength(selectedUserId) }
+ }
+
+ override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
+ withContext(backgroundDispatcher) {
+ if (isSuccessful) {
+ lockPatternUtils.reportSuccessfulPasswordAttempt(selectedUserId)
+ _hasLockoutOccurred.value = false
+ } else {
+ lockPatternUtils.reportFailedPasswordAttempt(selectedUserId)
+ }
+ _failedAuthenticationAttempts.value = getFailedAuthenticationAttemptCount()
+ }
+ }
+
+ override suspend fun reportLockoutStarted(durationMs: Int) {
+ lockPatternUtils.setLockoutAttemptDeadline(selectedUserId, durationMs)
+ withContext(backgroundDispatcher) {
+ lockPatternUtils.reportPasswordLockout(durationMs, selectedUserId)
+ }
+ _hasLockoutOccurred.value = true
+ }
+
+ private suspend fun getFailedAuthenticationAttemptCount(): Int {
+ return withContext(backgroundDispatcher) {
+ lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId)
+ }
+ }
+
+ override suspend fun getMaxFailedUnlockAttemptsForWipe(): Int {
+ return withContext(backgroundDispatcher) {
+ lockPatternUtils.getMaximumFailedPasswordsForWipe(selectedUserId)
+ }
+ }
+
+ override suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int {
+ return withContext(backgroundDispatcher) {
+ devicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(selectedUserId)
+ }
+ }
+
private val selectedUserId: Int
- get() = userRepository.getSelectedUserInfo().id
+ @UserIdInt get() = userRepository.getSelectedUserInfo().id
/**
* Returns a [StateFlow] that's automatically kept fresh. The passed-in [getFreshValue] is
@@ -337,24 +370,18 @@
return flow.asStateFlow()
}
- /**
- * Returns the authentication method for the given user ID.
- *
- * WARNING: this is actually a blocking IPC/"binder" call that's expensive to do on the main
- * thread. We keep it not marked as `suspend` because we want to be able to run this without a
- * `runBlocking` which has a ton of performance/blocking problems.
- */
- private fun blockingAuthenticationMethodInternal(
- userId: Int,
- ): AuthenticationMethodModel {
- return when (getSecurityMode.apply(userId)) {
- KeyguardSecurityModel.SecurityMode.PIN -> AuthenticationMethodModel.Pin
- KeyguardSecurityModel.SecurityMode.SimPin,
- KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Sim
- KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password
- KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern
- KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None
- KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!")
+ /** Returns the authentication method for the given user ID. */
+ private suspend fun getAuthenticationMethod(@UserIdInt userId: Int): AuthenticationMethodModel {
+ return withContext(backgroundDispatcher) {
+ when (getSecurityMode.apply(userId)) {
+ KeyguardSecurityModel.SecurityMode.PIN -> Pin
+ KeyguardSecurityModel.SecurityMode.SimPin,
+ KeyguardSecurityModel.SecurityMode.SimPuk -> Sim
+ KeyguardSecurityModel.SecurityMode.Password -> Password
+ KeyguardSecurityModel.SecurityMode.Pattern -> Pattern
+ KeyguardSecurityModel.SecurityMode.None -> None
+ KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!")
+ }
}
}
}
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 797154e..fdccad1 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,36 +16,34 @@
package com.android.systemui.authentication.domain.interactor
-import com.android.app.tracing.TraceUtils.Companion.withContext
+import android.os.UserHandle
+import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.systemui.authentication.data.repository.AuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel.WipeTarget
import com.android.systemui.dagger.SysUISingleton
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.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
-import kotlin.math.ceil
import kotlin.math.max
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.async
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/**
* Hosts application business logic related to user authentication.
@@ -59,10 +57,8 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
private val repository: AuthenticationRepository,
- private val userRepository: UserRepository,
- private val clock: SystemClock,
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
/**
* The currently-configured authentication method. This determines how the authentication
@@ -85,13 +81,6 @@
val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod
/**
- * The current authentication lockout (aka "throttling") state, set when the user has to wait
- * before being able to try another authentication attempt. `null` indicates lockout isn't
- * active.
- */
- val lockout: StateFlow<AuthenticationLockoutModel?> = repository.lockout
-
- /**
* Whether the auto confirm feature is enabled for the currently-selected user.
*
* Note that the length of the PIN is also important to take into consideration, please see
@@ -130,26 +119,64 @@
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible
+ private val _onAuthenticationResult = MutableSharedFlow<Boolean>()
/**
* Emits the outcome (successful or unsuccessful) whenever a PIN/Pattern/Password security
* challenge is attempted by the user in order to unlock the device.
*/
- val authenticationChallengeResult: SharedFlow<Boolean> =
- repository.authenticationChallengeResult
+ val onAuthenticationResult: SharedFlow<Boolean> = _onAuthenticationResult.asSharedFlow()
/** Whether the "enhanced PIN privacy" setting is enabled for the current user. */
val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> = repository.isPinEnhancedPrivacyEnabled
- private var lockoutCountdownJob: Job? = null
+ /**
+ * The number of failed authentication attempts for the selected user since the last successful
+ * authentication.
+ */
+ val failedAuthenticationAttempts: StateFlow<Int> = repository.failedAuthenticationAttempts
- init {
- applicationScope.launch {
- userRepository.selectedUserInfo
- .map { it.id }
- .distinctUntilChanged()
- .collect { onSelectedUserChanged() }
+ /**
+ * Timestamp for when the current lockout (aka "throttling") will end, allowing the user to
+ * attempt authentication again. Returns `null` if no lockout is active.
+ *
+ * To be notified whenever a lockout is started, the caller should subscribe to
+ * [onAuthenticationResult].
+ *
+ * Note that the value is in milliseconds and matches [SystemClock.elapsedRealtime].
+ *
+ * Also note that the value may change when the selected user is changed.
+ */
+ val lockoutEndTimestamp: Long?
+ get() = repository.lockoutEndTimestamp
+
+ /**
+ * Models an imminent wipe risk to the user, profile, or device upon further unsuccessful
+ * authentication attempts.
+ *
+ * Returns `null` when there is no risk of wipe yet, or when there's no wipe policy set by the
+ * DevicePolicyManager.
+ */
+ val upcomingWipe: Flow<AuthenticationWipeModel?> =
+ repository.failedAuthenticationAttempts.map { failedAttempts ->
+ val failedAttemptsBeforeWipe = repository.getMaxFailedUnlockAttemptsForWipe()
+ if (failedAttemptsBeforeWipe == 0) {
+ return@map null // There is no restriction.
+ }
+
+ // The user has a DevicePolicyManager that requests a user/profile to be wiped after N
+ // attempts. Once the grace period is reached, show a dialog every time as a clear
+ // warning until the deletion fires.
+ val remainingAttemptsBeforeWipe = max(0, failedAttemptsBeforeWipe - failedAttempts)
+ if (remainingAttemptsBeforeWipe >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ return@map null // There is no current risk of wiping the device.
+ }
+
+ AuthenticationWipeModel(
+ wipeTarget = getWipeTarget(),
+ failedAttempts = failedAttempts,
+ remainingAttempts = remainingAttemptsBeforeWipe,
+ )
}
- }
/**
* Returns the currently-configured authentication method. This determines how the
@@ -166,7 +193,8 @@
suspend fun getAuthenticationMethod() = repository.getAuthenticationMethod()
/**
- * Attempts to authenticate the user and unlock the device.
+ * Attempts to authenticate the user and unlock the device. May trigger lockout or wipe the
+ * user/profile/device data upon failure.
*
* If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method
* supports auto-confirming, and the input's length is at least the required length. Otherwise,
@@ -187,21 +215,7 @@
}
val authMethod = getAuthenticationMethod()
- val skipCheck =
- when {
- // Lockout is active, the UI layer should not have called this; skip the attempt.
- lockout.value != null -> true
- // The input is too short; skip the attempt.
- input.isTooShort(authMethod) -> true
- // Auto-confirm attempt when the feature is not enabled; skip the attempt.
- tryAutoConfirm && !isAutoConfirmEnabled.value -> true
- // Auto-confirm should skip the attempt if the pin entered is too short.
- tryAutoConfirm &&
- authMethod == AuthenticationMethodModel.Pin &&
- input.size < repository.getPinLength() -> true
- else -> false
- }
- if (skipCheck) {
+ if (shouldSkipAuthenticationAttempt(authMethod, tryAutoConfirm, input.size)) {
return AuthenticationResult.SKIPPED
}
@@ -210,102 +224,85 @@
val authenticationResult = repository.checkCredential(credential)
credential.zeroize()
- if (authenticationResult.isSuccessful || !tryAutoConfirm) {
- repository.reportAuthenticationAttempt(
- isSuccessful = authenticationResult.isSuccessful,
- )
- }
-
- // Check if lockout should start and, if so, kick off the countdown:
- if (!authenticationResult.isSuccessful && authenticationResult.lockoutDurationMs > 0) {
- repository.apply {
- setLockoutDuration(durationMs = authenticationResult.lockoutDurationMs)
- reportLockoutStarted(durationMs = authenticationResult.lockoutDurationMs)
- hasLockoutOccurred.value = true
- }
- startLockoutCountdown()
- }
-
if (authenticationResult.isSuccessful) {
- // Since authentication succeeded, refresh lockout to make sure the state is completely
- // reflecting the upstream source of truth.
- refreshLockout()
-
- repository.hasLockoutOccurred.value = false
+ repository.reportAuthenticationAttempt(isSuccessful = true)
+ _onAuthenticationResult.emit(true)
+ return AuthenticationResult.SUCCEEDED
}
- return if (authenticationResult.isSuccessful) {
- AuthenticationResult.SUCCEEDED
- } else {
- AuthenticationResult.FAILED
+ // Authentication failed.
+
+ if (tryAutoConfirm) {
+ // Auto-confirm is active, the failed attempt should have no side-effects.
+ return AuthenticationResult.FAILED
}
+
+ repository.reportAuthenticationAttempt(isSuccessful = false)
+
+ if (authenticationResult.lockoutDurationMs > 0) {
+ // Lockout has been triggered.
+ repository.reportLockoutStarted(authenticationResult.lockoutDurationMs)
+ }
+
+ _onAuthenticationResult.emit(false)
+ return AuthenticationResult.FAILED
}
- private fun List<Any>.isTooShort(authMethod: AuthenticationMethodModel): Boolean {
- return when (authMethod) {
- AuthenticationMethodModel.Pattern -> size < repository.minPatternLength
- AuthenticationMethodModel.Password -> size < repository.minPasswordLength
+ private suspend fun shouldSkipAuthenticationAttempt(
+ authenticationMethod: AuthenticationMethodModel,
+ isAutoConfirmAttempt: Boolean,
+ inputLength: Int,
+ ): Boolean {
+ return when {
+ // Lockout is active, the UI layer should not have called this; skip the attempt.
+ repository.lockoutEndTimestamp != null -> true
+ // Auto-confirm attempt when the feature is not enabled; skip the attempt.
+ isAutoConfirmAttempt && !isAutoConfirmEnabled.value -> true
+ // The pin is too short; skip only if this is an auto-confirm attempt.
+ authenticationMethod == Pin && authenticationMethod.isInputTooShort(inputLength) ->
+ isAutoConfirmAttempt
+ // The input is too short.
+ authenticationMethod.isInputTooShort(inputLength) -> true
else -> false
}
}
- /** Starts refreshing the lockout state every second. */
- private suspend fun startLockoutCountdown() {
- cancelLockoutCountdown()
- lockoutCountdownJob =
- applicationScope.launch {
- while (refreshLockout()) {
- delay(1.seconds.inWholeMilliseconds)
- }
- }
- }
-
- /** Cancels any lockout state countdown started in [startLockoutCountdown]. */
- private fun cancelLockoutCountdown() {
- lockoutCountdownJob?.cancel()
- lockoutCountdownJob = null
- }
-
- /** Notifies that the currently-selected user has changed. */
- private suspend fun onSelectedUserChanged() {
- cancelLockoutCountdown()
- if (refreshLockout()) {
- startLockoutCountdown()
+ private suspend fun AuthenticationMethodModel.isInputTooShort(inputLength: Int): Boolean {
+ return when (this) {
+ Pattern -> inputLength < repository.minPatternLength
+ Password -> inputLength < repository.minPasswordLength
+ Pin -> inputLength < repository.getPinLength()
+ else -> false
}
}
/**
- * Refreshes the lockout state, hydrating the repository with the latest state.
- *
- * @return Whether lockout is active or not.
+ * @return Whether the current user, managed profile or whole device is next at risk of wipe.
*/
- private suspend fun refreshLockout(): Boolean {
- withContext("$TAG#refreshLockout", backgroundDispatcher) {
- val failedAttemptCount = async { repository.getFailedAuthenticationAttemptCount() }
- val deadline = async { repository.getLockoutEndTimestamp() }
- val remainingMs = max(0, deadline.await() - clock.elapsedRealtime())
- repository.lockout.value =
- if (remainingMs > 0) {
- AuthenticationLockoutModel(
- failedAttemptCount = failedAttemptCount.await(),
- remainingSeconds = ceil(remainingMs / 1000f).toInt(),
- )
+ private suspend fun getWipeTarget(): WipeTarget {
+ // Check which profile has the strictest policy for failed authentication attempts.
+ val userToBeWiped = repository.getProfileWithMinFailedUnlockAttemptsForWipe()
+ return when (userToBeWiped) {
+ selectedUserInteractor.getSelectedUserId() ->
+ if (userToBeWiped == UserHandle.USER_SYSTEM) {
+ WipeTarget.WholeDevice
} else {
- null // Lockout ended.
+ WipeTarget.User
}
+
+ // Shouldn't happen at this stage; this is to maintain legacy behavior.
+ UserHandle.USER_NULL -> WipeTarget.WholeDevice
+ else -> WipeTarget.ManagedProfile
}
- return repository.lockout.value != null
}
private fun AuthenticationMethodModel.createCredential(
input: List<Any>
): LockscreenCredential? {
return when (this) {
- is AuthenticationMethodModel.Pin ->
- LockscreenCredential.createPin(input.joinToString(""))
- is AuthenticationMethodModel.Password ->
- LockscreenCredential.createPassword(input.joinToString(""))
- is AuthenticationMethodModel.Pattern ->
+ is Pin -> LockscreenCredential.createPin(input.joinToString(""))
+ is Password -> LockscreenCredential.createPassword(input.joinToString(""))
+ is Pattern ->
LockscreenCredential.createPattern(
input
.map { it as AuthenticationPatternCoordinate }
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 3552a19..4e45fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -30,13 +30,13 @@
* Device doesn't use a secure authentication method. Either there is no lockscreen or the lock
* screen can be swiped away when displayed.
*/
- object None : AuthenticationMethodModel(isSecure = false)
+ data object None : AuthenticationMethodModel(isSecure = false)
- object Pin : AuthenticationMethodModel(isSecure = true)
+ data object Pin : AuthenticationMethodModel(isSecure = true)
- object Password : AuthenticationMethodModel(isSecure = true)
+ data object Password : AuthenticationMethodModel(isSecure = true)
- object Pattern : AuthenticationMethodModel(isSecure = true)
+ data object Pattern : AuthenticationMethodModel(isSecure = true)
- object Sim : AuthenticationMethodModel(isSecure = true)
+ data object Sim : AuthenticationMethodModel(isSecure = true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationWipeModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationWipeModel.kt
new file mode 100644
index 0000000..7a2b83f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationWipeModel.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.authentication.shared.model
+
+import androidx.annotation.StringRes
+import com.android.systemui.res.R
+
+/**
+ * Some users have a DevicePolicyManager that requests a user/profile to be wiped after N
+ * unsuccessful authentication attempts. This models the pre-wipe state, so that a warning can be
+ * shown.
+ */
+data class AuthenticationWipeModel(
+ /** Indicates what part of the user data will be removed. */
+ val wipeTarget: WipeTarget,
+
+ /** Unsuccessful authentication attempts since the last successful device entry. */
+ val failedAttempts: Int,
+
+ /**
+ * Remaining failed authentication attempts before wipe is triggered. 0 indicates a wipe is
+ * imminent, no more authentication attempts are allowed.
+ */
+ val remainingAttempts: Int,
+) {
+ sealed class WipeTarget(
+ @StringRes val messageIdForAlmostWipe: Int,
+ @StringRes val messageIdForWipe: Int,
+ ) {
+ /** The work profile will be removed, which will delete all profile data. */
+ data object ManagedProfile :
+ WipeTarget(
+ messageIdForAlmostWipe = R.string.kg_failed_attempts_almost_at_erase_profile,
+ messageIdForWipe = R.string.kg_failed_attempts_now_erasing_profile,
+ )
+
+ /** The user will be removed, which will delete all user data. */
+ data object User :
+ WipeTarget(
+ messageIdForAlmostWipe = R.string.kg_failed_attempts_almost_at_erase_user,
+ messageIdForWipe = R.string.kg_failed_attempts_now_erasing_user,
+ )
+
+ /** The device will be reset and all data will be deleted. */
+ data object WholeDevice :
+ WipeTarget(
+ messageIdForAlmostWipe = R.string.kg_failed_attempts_almost_at_wipe,
+ messageIdForWipe = R.string.kg_failed_attempts_now_wiping,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 5fba761..8a1a2da 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -84,6 +84,7 @@
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
@@ -114,8 +115,12 @@
* {@link com.android.keyguard.KeyguardUpdateMonitor}
*/
@SysUISingleton
-public class AuthController implements CoreStartable, CommandQueue.Callbacks,
- AuthDialogCallback, DozeReceiver {
+public class AuthController implements
+ CoreStartable,
+ ConfigurationController.ConfigurationListener,
+ CommandQueue.Callbacks,
+ AuthDialogCallback,
+ DozeReceiver {
private static final String TAG = "AuthController";
private static final boolean DEBUG = true;
@@ -1297,7 +1302,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
updateSensorLocations();
// TODO(b/287311775): consider removing this to retain the UI cleanly vs re-creating
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 66fb8ca..7d9ec08 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -23,16 +23,14 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.systemui.Dumpable
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.ViewController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.io.PrintWriter
@@ -49,7 +47,7 @@
abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
view: T,
protected val statusBarStateController: StatusBarStateController,
- protected val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ protected val shadeInteractor: ShadeInteractor,
protected val dialogManager: SystemUIDialogManager,
private val dumpManager: DumpManager
) : ViewController<T>(view), Dumpable {
@@ -94,20 +92,18 @@
// can make the view not visible; and we still want to listen for events
// that may make the view visible again.
repeatOnLifecycle(Lifecycle.State.CREATED) {
- listenForBouncerExpansion(this)
+ listenForShadeExpansion(this)
}
}
}
@VisibleForTesting
- open suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
+ suspend fun listenForShadeExpansion(scope: CoroutineScope): Job {
return scope.launch {
- primaryBouncerInteractor.bouncerExpansion.map { 1f - it }.collect { expansion: Float ->
- if (statusBarStateController.state != SHADE) {
- notificationShadeVisible = expansion > 0f
- view.onExpansionChanged(expansion)
- updatePauseAuth()
- }
+ shadeInteractor.anyExpansion.collect { expansion ->
+ notificationShadeVisible = expansion > 0f
+ view.onExpansionChanged(expansion)
+ updatePauseAuth()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
index 03749a9..e7b0d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.biometrics
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.phone.SystemUIDialogManager
/**
@@ -26,13 +26,13 @@
class UdfpsBpViewController(
view: UdfpsBpView,
statusBarStateController: StatusBarStateController,
- primaryBouncerInteractor: PrimaryBouncerInteractor,
+ shadeInteractor: ShadeInteractor,
systemUIDialogManager: SystemUIDialogManager,
dumpManager: DumpManager
) : UdfpsAnimationViewController<UdfpsBpView>(
view,
statusBarStateController,
- primaryBouncerInteractor,
+ shadeInteractor,
systemUIDialogManager,
dumpManager
) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 65668b5..2fd13b3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -86,11 +86,10 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter;
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -115,7 +114,6 @@
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import javax.inject.Provider;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
@@ -152,7 +150,6 @@
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
- @NonNull private final Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
@NonNull private final VibratorHelper mVibrator;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@@ -166,6 +163,7 @@
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ @NonNull private final ShadeInteractor mShadeInteractor;
@Nullable private final TouchProcessor mTouchProcessor;
@NonNull private final SessionTracker mSessionTracker;
@NonNull private final Lazy<DeviceEntryUdfpsTouchOverlayViewModel>
@@ -294,7 +292,8 @@
mKeyguardTransitionInteractor,
mSelectedUserInteractor,
mDeviceEntryUdfpsTouchOverlayViewModel,
- mDefaultUdfpsTouchOverlayViewModel
+ mDefaultUdfpsTouchOverlayViewModel,
+ mShadeInteractor
)));
}
@@ -623,7 +622,7 @@
} else {
onKeyguard = mOverlay != null
&& mOverlay.getAnimationViewController()
- instanceof UdfpsKeyguardViewControllerAdapter;
+ instanceof UdfpsKeyguardViewControllerLegacy;
}
return onKeyguard
&& mKeyguardStateController.canDismissLockScreen()
@@ -660,13 +659,13 @@
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull @BiometricsBackground Executor biometricsExecutor,
@NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
+ @NonNull ShadeInteractor shadeInteractor,
@NonNull SinglePointerTouchProcessor singlePointerTouchProcessor,
@NonNull SessionTracker sessionTracker,
@NonNull AlternateBouncerInteractor alternateBouncerInteractor,
@NonNull InputManager inputManager,
@NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
- @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider,
@NonNull SelectedUserInteractor selectedUserInteractor,
@NonNull FpsUnlockTracker fpsUnlockTracker,
@NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -710,6 +709,7 @@
mBiometricExecutor = biometricsExecutor;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
+ mShadeInteractor = shadeInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
mInputManager = inputManager;
mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
@@ -737,7 +737,6 @@
return Unit.INSTANCE;
});
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
- mUdfpsKeyguardViewModels = udfpsKeyguardViewModelsProvider;
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index dae6d08..b94a177 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -55,9 +55,9 @@
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
@@ -108,6 +108,7 @@
private val selectedUserInteractor: SelectedUserInteractor,
private val deviceEntryUdfpsTouchOverlayViewModel: Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
+ private val shadeInteractor: ShadeInteractor,
) {
private var overlayViewLegacy: UdfpsView? = null
private set
@@ -278,7 +279,7 @@
updateAccessibilityViewLocation(sensorBounds)
},
statusBarStateController,
- primaryBouncerInteractor,
+ shadeInteractor,
dialogManager,
dumpManager
)
@@ -304,6 +305,7 @@
udfpsKeyguardAccessibilityDelegate,
selectedUserInteractor,
transitionInteractor,
+ shadeInteractor,
)
}
REASON_AUTH_BP -> {
@@ -311,7 +313,7 @@
UdfpsBpViewController(
view.addUdfpsView(R.layout.udfps_bp_view),
statusBarStateController,
- primaryBouncerInteractor,
+ shadeInteractor,
dialogManager,
dumpManager
)
@@ -321,7 +323,7 @@
UdfpsFpmEmptyViewController(
view.addUdfpsView(R.layout.udfps_fpm_empty_view),
statusBarStateController,
- primaryBouncerInteractor,
+ shadeInteractor,
dialogManager,
dumpManager
)
@@ -438,7 +440,7 @@
if (DeviceEntryUdfpsRefactor.isEnabled) {
!keyguardStateController.isShowing
} else {
- animation !is UdfpsKeyguardViewControllerAdapter
+ animation !is UdfpsKeyguardViewControllerLegacy
}
if (keyguardNotShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
index 88002e7..ab3fbb1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.biometrics
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.phone.SystemUIDialogManager
/**
@@ -28,13 +28,13 @@
class UdfpsFpmEmptyViewController(
view: UdfpsFpmEmptyView,
statusBarStateController: StatusBarStateController,
- primaryBouncerInteractor: PrimaryBouncerInteractor,
+ shadeInteractor: ShadeInteractor,
systemUIDialogManager: SystemUIDialogManager,
dumpManager: DumpManager
) : UdfpsAnimationViewController<UdfpsFpmEmptyView>(
view,
statusBarStateController,
- primaryBouncerInteractor,
+ shadeInteractor,
systemUIDialogManager,
dumpManager
) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 63fe26a..9f17024 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -33,10 +33,10 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -69,20 +69,20 @@
systemUIDialogManager: SystemUIDialogManager,
private val udfpsController: UdfpsController,
private val activityLaunchAnimator: ActivityLaunchAnimator,
- primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
private val selectedUserInteractor: SelectedUserInteractor,
private val transitionInteractor: KeyguardTransitionInteractor,
+ shadeInteractor: ShadeInteractor,
) :
UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
view,
statusBarStateController,
- primaryBouncerInteractor,
+ shadeInteractor,
systemUIDialogManager,
dumpManager,
- ),
- UdfpsKeyguardViewControllerAdapter {
+ ) {
private val uniqueIdentifier = this.toString()
private var showingUdfpsBouncer = false
private var udfpsRequested = false
@@ -199,11 +199,27 @@
listenForAodToOccludedTransitions(this)
listenForAlternateBouncerToAodTransitions(this)
listenForDreamingToAodTransitions(this)
+ listenForPrimaryBouncerToAodTransitions(this)
}
}
}
@VisibleForTesting
+ suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job {
+ return scope.launch {
+ transitionInteractor
+ .transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD)
+ .collect { transitionStep ->
+ view.onDozeAmountChanged(
+ transitionStep.value,
+ transitionStep.value,
+ ANIMATE_APPEAR_ON_SCREEN_OFF,
+ )
+ }
+ }
+ }
+
+ @VisibleForTesting
suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
transitionInteractor.transition(KeyguardState.DREAMING, KeyguardState.AOD).collect {
@@ -305,7 +321,7 @@
}
@VisibleForTesting
- override suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
+ suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
return scope.launch {
primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
inputBouncerExpansion = bouncerExpansion
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 8ae6f87..307b985 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -19,6 +19,7 @@
import android.content.res.Resources
import com.android.internal.R
import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.EllipseOverlapDetectorParams
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
@@ -38,18 +39,30 @@
import com.android.systemui.biometrics.udfps.OverlapDetector
import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.ThreadFactory
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
import java.util.concurrent.Executor
import javax.inject.Qualifier
/** Dagger module for all things biometric. */
@Module
interface BiometricsModule {
+ /** Starts AuthController. */
+ @Binds
+ @IntoMap
+ @ClassKey(AuthController::class)
+ fun bindAuthControllerStartable(service: AuthController): CoreStartable
+
+ /** Listen to config changes for AuthController. */
+ @Binds
+ @IntoSet
+ fun bindAuthControllerConfigChanges(service: AuthController): ConfigurationListener
@Binds
@IntoMap
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 a8c9446..c36e0e2 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
@@ -47,6 +47,7 @@
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.flow.combine
@@ -59,49 +60,56 @@
constructor(
@Application private val applicationScope: CoroutineScope,
@Application private val applicationContext: Context,
- private val biometricStatusInteractor: BiometricStatusInteractor,
- private val displayStateInteractor: DisplayStateInteractor,
- private val deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
- private val fpsUnlockTracker: FpsUnlockTracker,
- private val layoutInflater: LayoutInflater,
- private val sideFpsProgressBarViewModel: SideFpsProgressBarViewModel,
- private val sfpsSensorInteractor: SideFpsSensorInteractor,
- private val windowManager: WindowManager
+ private val biometricStatusInteractor: Lazy<BiometricStatusInteractor>,
+ private val displayStateInteractor: Lazy<DisplayStateInteractor>,
+ private val deviceEntrySideFpsOverlayInteractor: Lazy<DeviceEntrySideFpsOverlayInteractor>,
+ private val fpsUnlockTracker: Lazy<FpsUnlockTracker>,
+ private val layoutInflater: Lazy<LayoutInflater>,
+ private val sideFpsProgressBarViewModel: Lazy<SideFpsProgressBarViewModel>,
+ private val sfpsSensorInteractor: Lazy<SideFpsSensorInteractor>,
+ private val windowManager: Lazy<WindowManager>
) : CoreStartable {
override fun start() {
if (!SideFpsControllerRefactor.isEnabled) {
return
}
+
applicationScope
.launch {
- combine(
- biometricStatusInteractor.sfpsAuthenticationReason,
- deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry,
- sideFpsProgressBarViewModel.isVisible,
- ::Triple
- )
- .sample(displayStateInteractor.isInRearDisplayMode, ::Pair)
- .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
- val (
- systemServerAuthReason,
- showIndicatorForDeviceEntry,
- progressBarIsVisible) =
- combinedFlows
- if (!isInRearDisplayMode) {
- if (progressBarIsVisible) {
- hide()
- } else if (systemServerAuthReason != NotRunning) {
- show()
- } else if (showIndicatorForDeviceEntry) {
- show()
- } else {
- hide()
+ 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
+ if (!isInRearDisplayMode) {
+ if (progressBarIsVisible) {
+ hide()
+ } else if (systemServerAuthReason != NotRunning) {
+ show()
+ } else if (showIndicatorForDeviceEntry) {
+ show()
+ } else {
+ hide()
+ }
+ }
}
- }
}
+ }
}
- .invokeOnCompletion { fpsUnlockTracker.stopTracking() }
+ .invokeOnCompletion { fpsUnlockTracker.get().stopTracking() }
}
private var overlayView: View? = null
@@ -113,29 +121,29 @@
if (it.isAttachedToWindow) {
lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
lottie?.pauseAnimation()
- windowManager.removeView(it)
+ windowManager.get().removeView(it)
}
}
- overlayView = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+ overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
val overlayViewModel =
SideFpsOverlayViewModel(
applicationContext,
- biometricStatusInteractor,
- deviceEntrySideFpsOverlayInteractor,
- displayStateInteractor,
- sfpsSensorInteractor,
- sideFpsProgressBarViewModel
+ biometricStatusInteractor.get(),
+ deviceEntrySideFpsOverlayInteractor.get(),
+ displayStateInteractor.get(),
+ sfpsSensorInteractor.get(),
+ sideFpsProgressBarViewModel.get()
)
- bind(overlayView!!, overlayViewModel, fpsUnlockTracker, windowManager)
+ bind(overlayView!!, overlayViewModel, fpsUnlockTracker.get(), windowManager.get())
overlayView!!.visibility = View.INVISIBLE
- windowManager.addView(overlayView, overlayViewModel.defaultOverlayViewParams)
+ windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
}
/** Hide the side fingerprint sensor indicator */
private fun hide() {
if (overlayView != null) {
- windowManager.removeView(overlayView)
+ windowManager.get().removeView(overlayView)
overlayView = null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
deleted file mode 100644
index 6f4e1a3..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
+++ /dev/null
@@ -1,78 +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.biometrics.ui.controller
-
-import com.android.systemui.biometrics.UdfpsAnimationViewController
-import com.android.systemui.biometrics.UdfpsKeyguardView
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-/** Class that coordinates non-HBM animations during keyguard authentication. */
-@ExperimentalCoroutinesApi
-open class UdfpsKeyguardViewController(
- val view: UdfpsKeyguardView,
- statusBarStateController: StatusBarStateController,
- primaryBouncerInteractor: PrimaryBouncerInteractor,
- systemUIDialogManager: SystemUIDialogManager,
- dumpManager: DumpManager,
- private val alternateBouncerInteractor: AlternateBouncerInteractor,
- udfpsKeyguardViewModels: UdfpsKeyguardViewModels,
-) :
- UdfpsAnimationViewController<UdfpsKeyguardView>(
- view,
- statusBarStateController,
- primaryBouncerInteractor,
- systemUIDialogManager,
- dumpManager,
- ),
- UdfpsKeyguardViewControllerAdapter {
- private val uniqueIdentifier = this.toString()
- override val tag: String
- get() = TAG
-
- init {
- udfpsKeyguardViewModels.bindViews(view)
- }
-
- public override fun onViewAttached() {
- super.onViewAttached()
- alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier)
- }
-
- public override fun onViewDetached() {
- super.onViewDetached()
- alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier)
- }
-
- override fun shouldPauseAuth(): Boolean {
- return !view.isVisible()
- }
-
- override fun listenForTouchesOutsideView(): Boolean {
- return true
- }
-
- companion object {
- private const val TAG = "UdfpsKeyguardViewController"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 724c0fe..81d822f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -19,8 +19,11 @@
import android.content.Context
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
-import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.classifier.FalsingClassifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
@@ -29,17 +32,15 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
/** Encapsulates business logic and application state accessing use-cases. */
@@ -52,33 +53,12 @@
private val repository: BouncerRepository,
private val authenticationInteractor: AuthenticationInteractor,
private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
- flags: SceneContainerFlags,
private val falsingInteractor: FalsingInteractor,
private val powerInteractor: PowerInteractor,
private val simBouncerInteractor: SimBouncerInteractor,
) {
-
- /** The user-facing message to show in the bouncer. */
- val message: StateFlow<String?> =
- combine(repository.message, authenticationInteractor.lockout) { message, lockout ->
- messageOrLockoutMessage(message, lockout)
- }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue =
- messageOrLockoutMessage(
- repository.message.value,
- authenticationInteractor.lockout.value,
- )
- )
-
- /**
- * The current authentication lockout (aka "throttling") state, set when the user has to wait
- * before being able to try another authentication attempt. `null` indicates lockout isn't
- * active.
- */
- val lockout: StateFlow<AuthenticationLockoutModel?> = authenticationInteractor.lockout
+ /** The user-facing message to show in the bouncer when lockout is not active. */
+ val message: StateFlow<String?> = repository.message
/** Whether the auto confirm feature is enabled for the currently-selected user. */
val isAutoConfirmEnabled: StateFlow<Boolean> = authenticationInteractor.isAutoConfirmEnabled
@@ -101,18 +81,13 @@
/** Emits a [Unit] each time the IME (keyboard) is hidden by the user. */
val onImeHiddenByUser: SharedFlow<Unit> = _onImeHiddenByUser
- init {
- if (flags.isEnabled()) {
- // Clear the message if moved from locked-out to no-longer locked-out.
- applicationScope.launch {
- lockout.pairwise().collect { (previous, current) ->
- if (previous != null && current == null) {
- clearMessage()
- }
- }
+ /** Emits a [Unit] each time a lockout is started for the selected user. */
+ val onLockoutStarted: Flow<Unit> =
+ authenticationInteractor.onAuthenticationResult
+ .filter { successfullyAuthenticated ->
+ !successfullyAuthenticated && authenticationInteractor.lockoutEndTimestamp != null
}
- }
- }
+ .map {}
/** Notifies that the user has places down a pointer, not necessarily dragging just yet. */
fun onDown() {
@@ -187,8 +162,8 @@
return AuthenticationResult.SKIPPED
}
- if (authenticationInteractor.getAuthenticationMethod() == AuthenticationMethodModel.Sim) {
- // We authenticate sim in SimInteractor
+ if (authenticationInteractor.getAuthenticationMethod() == Sim) {
+ // SIM is authenticated in SimBouncerInteractor.
return AuthenticationResult.SKIPPED
}
@@ -196,30 +171,32 @@
// view-models, whose lifecycle (and thus scope) is shorter than this interactor.
// This allows the task to continue running properly even when the calling scope has been
// cancelled.
- return applicationScope
- .async {
- val authResult = authenticationInteractor.authenticate(input, tryAutoConfirm)
- if (
- authResult == AuthenticationResult.FAILED ||
- (authResult == AuthenticationResult.SKIPPED && !tryAutoConfirm)
- ) {
- showErrorMessage()
- }
- authResult
- }
- .await()
+ val authResult =
+ applicationScope
+ .async { authenticationInteractor.authenticate(input, tryAutoConfirm) }
+ .await()
+
+ if (authenticationInteractor.lockoutEndTimestamp != null) {
+ clearMessage()
+ } else if (
+ authResult == AuthenticationResult.FAILED ||
+ (authResult == AuthenticationResult.SKIPPED && !tryAutoConfirm)
+ ) {
+ showWrongInputMessage()
+ }
+ return authResult
}
/**
- * Shows the error message.
+ * Shows the a message notifying the user that their credentials input is wrong.
*
* Callers should use this instead of [authenticate] when they know ahead of time that an auth
* attempt will fail but aren't interested in the other side effects like triggering lockout.
* For example, if the user entered a pattern that's too short, the system can show the error
* message without having the attempt trigger lockout.
*/
- private suspend fun showErrorMessage() {
- repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
+ private suspend fun showWrongInputMessage() {
+ repository.setMessage(wrongInputMessage(authenticationInteractor.getAuthenticationMethod()))
}
/** Notifies that the input method editor (software keyboard) has been hidden by the user. */
@@ -229,39 +206,19 @@
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
- is AuthenticationMethodModel.Sim -> simBouncerInteractor.getDefaultMessage()
- is AuthenticationMethodModel.Pin ->
- applicationContext.getString(R.string.keyguard_enter_your_pin)
- is AuthenticationMethodModel.Password ->
- applicationContext.getString(R.string.keyguard_enter_your_password)
- is AuthenticationMethodModel.Pattern ->
- applicationContext.getString(R.string.keyguard_enter_your_pattern)
+ is Sim -> simBouncerInteractor.getDefaultMessage()
+ is Pin -> applicationContext.getString(R.string.keyguard_enter_your_pin)
+ is Password -> applicationContext.getString(R.string.keyguard_enter_your_password)
+ is Pattern -> applicationContext.getString(R.string.keyguard_enter_your_pattern)
else -> ""
}
}
- private fun errorMessage(authMethod: AuthenticationMethodModel): String {
+ private fun wrongInputMessage(authMethod: AuthenticationMethodModel): String {
return when (authMethod) {
- is AuthenticationMethodModel.Pin -> applicationContext.getString(R.string.kg_wrong_pin)
- is AuthenticationMethodModel.Password ->
- applicationContext.getString(R.string.kg_wrong_password)
- is AuthenticationMethodModel.Pattern ->
- applicationContext.getString(R.string.kg_wrong_pattern)
- else -> ""
- }
- }
-
- private fun messageOrLockoutMessage(
- message: String?,
- lockoutModel: AuthenticationLockoutModel?,
- ): String {
- return when {
- lockoutModel != null ->
- applicationContext.getString(
- com.android.internal.R.string.lockscreen_too_many_failed_attempts_countdown,
- lockoutModel.remainingSeconds,
- )
- message != null -> message
+ is Pin -> applicationContext.getString(R.string.kg_wrong_pin)
+ is Password -> applicationContext.getString(R.string.kg_wrong_password)
+ is Pattern -> applicationContext.getString(R.string.kg_wrong_pattern)
else -> ""
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index f612f9a..b587ed8 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -22,6 +22,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Flags
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.BouncerMessageRepository
@@ -29,8 +30,6 @@
import com.android.systemui.bouncer.shared.model.Message
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
@@ -100,7 +99,6 @@
private val repository: BouncerMessageRepository,
private val userRepository: UserRepository,
private val countDownTimerUtil: CountDownTimerUtil,
- private val featureFlags: FeatureFlags,
private val updateMonitor: KeyguardUpdateMonitor,
trustRepository: TrustRepository,
biometricSettingsRepository: BiometricSettingsRepository,
@@ -229,7 +227,7 @@
}
fun onPrimaryAuthLockedOut(secondsBeforeLockoutReset: Long) {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ if (!Flags.revampedBouncerMessages()) return
val callback =
object : CountDownTimerCallback {
@@ -250,7 +248,7 @@
}
fun onPrimaryAuthIncorrectAttempt() {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ if (!Flags.revampedBouncerMessages()) return
repository.setMessage(
incorrectSecurityInput(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
@@ -258,21 +256,21 @@
}
fun setFingerprintAcquisitionMessage(value: String?) {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ if (!Flags.revampedBouncerMessages()) return
repository.setMessage(
defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setFaceAcquisitionMessage(value: String?) {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ if (!Flags.revampedBouncerMessages()) return
repository.setMessage(
defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
)
}
fun setCustomMessage(value: String?) {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ if (!Flags.revampedBouncerMessages()) return
repository.setMessage(
defaultMessage(currentSecurityMode, value, isFingerprintAuthCurrentlyAllowed.value)
@@ -283,7 +281,7 @@
get() = defaultMessage(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
fun onPrimaryBouncerUserInput() {
- if (!featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) return
+ if (!Flags.revampedBouncerMessages()) return
repository.setMessage(defaultMessage)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt
index 5a59012..b8e6ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt
@@ -23,11 +23,10 @@
import com.android.keyguard.BouncerKeyguardMessageArea
import com.android.keyguard.KeyguardMessageArea
import com.android.keyguard.KeyguardMessageAreaController
+import com.android.systemui.Flags
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.shared.model.Message
import com.android.systemui.bouncer.ui.BouncerMessageView
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.BouncerLogger
import kotlinx.coroutines.launch
@@ -39,10 +38,9 @@
interactor: BouncerMessageInteractor,
factory: KeyguardMessageAreaController.Factory,
bouncerLogger: BouncerLogger,
- featureFlags: FeatureFlags
) {
view.repeatWhenAttached {
- if (!featureFlags.isEnabled(Flags.REVAMPED_BOUNCER_MESSAGES)) {
+ if (!Flags.revampedBouncerMessages()) {
view.primaryMessageView?.disable()
view.secondaryMessageView?.disable()
return@repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 5dcd661..cc387e9 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -32,7 +32,6 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.bouncer.ui.BouncerViewDelegate
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.BouncerLogger
@@ -53,7 +52,6 @@
messageAreaControllerFactory: KeyguardMessageAreaController.Factory,
bouncerMessageInteractor: BouncerMessageInteractor,
bouncerLogger: BouncerLogger,
- featureFlags: FeatureFlags,
selectedUserInteractor: SelectedUserInteractor,
) {
// Builds the KeyguardSecurityContainerController from bouncer view group.
@@ -141,8 +139,7 @@
it.bindMessageView(
bouncerMessageInteractor,
messageAreaControllerFactory,
- bouncerLogger,
- featureFlags
+ bouncerLogger
)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 4b14343..4d686a1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -16,11 +16,15 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources
import android.content.Context
import android.graphics.Bitmap
import androidx.core.graphics.drawable.toBitmap
+import com.android.internal.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+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.domain.interactor.SimBouncerInteractor
@@ -34,19 +38,23 @@
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import com.android.systemui.user.ui.viewmodel.UserViewModel
+import com.android.systemui.util.time.SystemClock
import dagger.Module
import dagger.Provides
+import kotlin.math.ceil
+import kotlin.math.max
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.job
@@ -58,13 +66,15 @@
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
private val bouncerInteractor: BouncerInteractor,
- authenticationInteractor: AuthenticationInteractor,
+ private val simBouncerInteractor: SimBouncerInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
flags: SceneContainerFlags,
selectedUser: Flow<UserViewModel>,
users: Flow<List<UserViewModel>>,
userSwitcherMenu: Flow<List<UserActionViewModel>>,
- actionButtonInteractor: BouncerActionButtonInteractor,
- private val simBouncerInteractor: SimBouncerInteractor,
+ actionButton: Flow<BouncerActionButtonModel?>,
+ private val clock: SystemClock,
+ private val devicePolicyManager: DevicePolicyManager,
) {
val selectedUserImage: StateFlow<Bitmap?> =
selectedUser
@@ -104,18 +114,8 @@
val isUserSwitcherVisible: Boolean
get() = bouncerInteractor.isUserSwitcherVisible
- private val isInputEnabled: StateFlow<Boolean> =
- bouncerInteractor.lockout
- .map { it == null }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = bouncerInteractor.lockout.value == null,
- )
-
// Handle to the scope of the child ViewModel (stored in [authMethod]).
private var childViewModelScope: CoroutineScope? = null
- private val _dialogMessage = MutableStateFlow<String?>(null)
/** View-model for the current UI, based on the current authentication method. */
val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
@@ -131,26 +131,49 @@
* A message for a dialog to show when the user has attempted the wrong credential too many
* times and now must wait a while before attempting again.
*
- * If `null`, no dialog should be shown.
- *
- * Once the dialog is shown, the UI should call [onDialogDismissed] when the user dismisses this
- * dialog.
+ * If `null`, the lockout dialog should not be shown.
*/
- val dialogMessage: StateFlow<String?> = _dialogMessage.asStateFlow()
+ private val lockoutDialogMessage = MutableStateFlow<String?>(null)
- /** The user-facing message to show in the bouncer. */
- val message: StateFlow<MessageViewModel> =
- combine(bouncerInteractor.message, bouncerInteractor.lockout) { message, lockout ->
- toMessageViewModel(message, isLockedOut = lockout != null)
- }
+ /**
+ * A message for a dialog to show when the user has attempted the wrong credential too many
+ * times and their user/profile/device data is at risk of being wiped due to a Device Manager
+ * policy.
+ *
+ * If `null`, the wipe dialog should not be shown.
+ */
+ private val wipeDialogMessage = MutableStateFlow<String?>(null)
+
+ /**
+ * Models the dialog to be shown to the user, or `null` if no dialog should be shown.
+ *
+ * Once the dialog is shown, the UI should call [DialogViewModel.onDismiss] when the user
+ * dismisses this dialog.
+ */
+ val dialogViewModel: StateFlow<DialogViewModel?> =
+ combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue =
- toMessageViewModel(
- message = bouncerInteractor.message.value,
- isLockedOut = bouncerInteractor.lockout.value != null,
- ),
+ initialValue = createDialogViewModel(),
+ )
+
+ /**
+ * A message shown when the user has attempted the wrong credential too many times and now must
+ * wait a while before attempting to authenticate again.
+ *
+ * This is updated every second (countdown) during the lockout duration. When lockout is not
+ * active, this is `null` and no lockout message should be shown.
+ */
+ private val lockoutMessage = MutableStateFlow<String?>(null)
+
+ /** The user-facing message to show in the bouncer. */
+ val message: StateFlow<MessageViewModel> =
+ combine(bouncerInteractor.message, lockoutMessage) { _, _ -> createMessageViewModel() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = createMessageViewModel(),
)
/**
@@ -158,7 +181,7 @@
* be shown.
*/
val actionButton: StateFlow<BouncerActionButtonModel?> =
- actionButtonInteractor.actionButton.stateIn(
+ actionButton.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null
@@ -194,31 +217,81 @@
initialValue = isFoldSplitRequired(authMethodViewModel.value),
)
+ private val isInputEnabled: StateFlow<Boolean> =
+ lockoutMessage
+ .map { it == null }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = authenticationInteractor.lockoutEndTimestamp == null,
+ )
+
+ private var lockoutCountdownJob: Job? = null
+
init {
if (flags.isEnabled()) {
+ // Keeps the lockout dialog up-to-date.
applicationScope.launch {
- combine(bouncerInteractor.lockout, authMethodViewModel) {
- lockout,
- authMethodViewModel ->
- if (lockout != null && authMethodViewModel != null) {
- applicationContext.getString(
- authMethodViewModel.lockoutMessageId,
- lockout.failedAttemptCount,
- lockout.remainingSeconds,
- )
- } else {
- null
- }
- }
- .distinctUntilChanged()
- .collect { dialogMessage -> _dialogMessage.value = dialogMessage }
+ bouncerInteractor.onLockoutStarted.collect {
+ showLockoutDialog()
+ startLockoutCountdown()
+ }
+ }
+
+ applicationScope.launch {
+ // Update the lockout countdown whenever the selected user is switched.
+ selectedUser.collect { startLockoutCountdown() }
+ }
+
+ // Keeps the upcoming wipe dialog up-to-date.
+ applicationScope.launch {
+ authenticationInteractor.upcomingWipe.collect { wipeModel ->
+ wipeDialogMessage.value = wipeModel?.message
+ }
}
}
}
- /** Notifies that the dialog has been dismissed by the user. */
- fun onDialogDismissed() {
- _dialogMessage.value = null
+ private fun showLockoutDialog() {
+ applicationScope.launch {
+ val failedAttempts = authenticationInteractor.failedAuthenticationAttempts.value
+ lockoutDialogMessage.value =
+ authMethodViewModel.value?.lockoutMessageId?.let { messageId ->
+ applicationContext.getString(
+ messageId,
+ failedAttempts,
+ remainingLockoutSeconds()
+ )
+ }
+ }
+ }
+
+ /** Shows the countdown message and refreshes it every second. */
+ private fun startLockoutCountdown() {
+ lockoutCountdownJob?.cancel()
+ lockoutCountdownJob =
+ applicationScope.launch {
+ do {
+ val remainingSeconds = remainingLockoutSeconds()
+ lockoutMessage.value =
+ if (remainingSeconds > 0) {
+ applicationContext.getString(
+ R.string.lockscreen_too_many_failed_attempts_countdown,
+ remainingSeconds,
+ )
+ } else {
+ null
+ }
+ delay(1.seconds)
+ } while (remainingSeconds > 0)
+ lockoutCountdownJob = null
+ }
+ }
+
+ private fun remainingLockoutSeconds(): Int {
+ val endTimestampMs = authenticationInteractor.lockoutEndTimestamp ?: 0
+ val remainingMs = max(0, endTimestampMs - clock.elapsedRealtime())
+ return ceil(remainingMs / 1000f).toInt()
}
private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
@@ -229,12 +302,11 @@
return authMethod !is PasswordBouncerViewModel
}
- private fun toMessageViewModel(
- message: String?,
- isLockedOut: Boolean,
- ): MessageViewModel {
+ private fun createMessageViewModel(): MessageViewModel {
+ val isLockedOut = lockoutMessage.value != null
return MessageViewModel(
- text = message ?: "",
+ // A lockout message takes precedence over the non-lockout message.
+ text = lockoutMessage.value ?: bouncerInteractor.message.value ?: "",
isUpdateAnimated = !isLockedOut,
)
}
@@ -294,6 +366,71 @@
)
}
+ /**
+ * @return A message warning the user that the user/profile/device will be wiped upon a further
+ * [AuthenticationWipeModel.remainingAttempts] unsuccessful authentication attempts.
+ */
+ private fun AuthenticationWipeModel.getAlmostAtWipeMessage(): String {
+ val message =
+ applicationContext.getString(
+ wipeTarget.messageIdForAlmostWipe,
+ failedAttempts,
+ remainingAttempts,
+ )
+ return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+ devicePolicyManager.resources.getString(
+ DevicePolicyResources.Strings.SystemUi
+ .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE,
+ { message },
+ failedAttempts,
+ remainingAttempts,
+ )
+ ?: message
+ } else {
+ message
+ }
+ }
+
+ /**
+ * @return A message informing the user that their user/profile/device will be wiped promptly.
+ */
+ private fun AuthenticationWipeModel.getWipeMessage(): String {
+ val message = applicationContext.getString(wipeTarget.messageIdForWipe, failedAttempts)
+ return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+ devicePolicyManager.resources.getString(
+ DevicePolicyResources.Strings.SystemUi
+ .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
+ { message },
+ failedAttempts,
+ )
+ ?: message
+ } else {
+ message
+ }
+ }
+
+ private val AuthenticationWipeModel.message: String
+ get() = if (remainingAttempts > 0) getAlmostAtWipeMessage() else getWipeMessage()
+
+ private fun createDialogViewModel(): DialogViewModel? {
+ val wipeText = wipeDialogMessage.value
+ val lockoutText = lockoutDialogMessage.value
+ return when {
+ // The wipe dialog takes priority over the lockout dialog.
+ wipeText != null ->
+ DialogViewModel(
+ text = wipeText,
+ onDismiss = { wipeDialogMessage.value = null },
+ )
+ lockoutText != null ->
+ DialogViewModel(
+ text = lockoutText,
+ onDismiss = { lockoutDialogMessage.value = null },
+ )
+ else -> null // No dialog to show.
+ }
+ }
+
data class MessageViewModel(
val text: String,
@@ -306,6 +443,13 @@
val isUpdateAnimated: Boolean,
)
+ data class DialogViewModel(
+ val text: String,
+
+ /** Callback to run after the dialog has been dismissed by the user. */
+ val onDismiss: () -> Unit,
+ )
+
data class UserSwitcherDropdownItemViewModel(
val icon: Icon,
val text: Text,
@@ -323,24 +467,28 @@
@Application applicationScope: CoroutineScope,
@Main mainDispatcher: CoroutineDispatcher,
bouncerInteractor: BouncerInteractor,
+ simBouncerInteractor: SimBouncerInteractor,
authenticationInteractor: AuthenticationInteractor,
flags: SceneContainerFlags,
userSwitcherViewModel: UserSwitcherViewModel,
actionButtonInteractor: BouncerActionButtonInteractor,
- simBouncerInteractor: SimBouncerInteractor,
+ clock: SystemClock,
+ devicePolicyManager: DevicePolicyManager,
): BouncerViewModel {
return BouncerViewModel(
applicationContext = applicationContext,
applicationScope = applicationScope,
mainDispatcher = mainDispatcher,
bouncerInteractor = bouncerInteractor,
+ simBouncerInteractor = simBouncerInteractor,
authenticationInteractor = authenticationInteractor,
flags = flags,
selectedUser = userSwitcherViewModel.selectedUser,
users = userSwitcherViewModel.users,
userSwitcherMenu = userSwitcherViewModel.menu,
- actionButtonInteractor = actionButtonInteractor,
- simBouncerInteractor = simBouncerInteractor,
+ actionButton = actionButtonInteractor.actionButton,
+ clock = clock,
+ devicePolicyManager = devicePolicyManager,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index b682717..8e14778 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -56,13 +56,11 @@
/** Whether the UI should request focus on the text field element. */
val isTextFieldFocusRequested =
- combine(interactor.lockout, isTextFieldFocused) { throttling, hasFocus ->
- throttling == null && !hasFocus
- }
+ combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus -> hasInput && !hasFocus }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = interactor.lockout.value == null && !isTextFieldFocused.value,
+ initialValue = isInputEnabled.value && !isTextFieldFocused.value,
)
override fun onHidden() {
@@ -104,7 +102,7 @@
* hidden.
*/
suspend fun onImeVisibilityChanged(isVisible: Boolean) {
- if (isImeVisible && !isVisible && interactor.lockout.value == null) {
+ if (isImeVisible && !isVisible && isInputEnabled.value) {
interactor.onImeHiddenByUser()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index a12db6f..779446d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -117,10 +117,10 @@
fun updateItemRank(itemUid: Long, order: Int)
@Transaction
- fun updateWidgetOrder(ids: List<Int>) {
- ids.forEachIndexed { index, it ->
- val widget = getWidgetByIdNow(it)
- updateItemRank(widget.itemId, ids.size - index)
+ fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {
+ widgetIdToPriorityMap.forEach { (id, priority) ->
+ val widget = getWidgetByIdNow(id)
+ updateItemRank(widget.itemId, priority)
}
}
@@ -129,7 +129,7 @@
return insertWidget(
widgetId = widgetId,
componentName = provider.flattenToString(),
- insertItemRank(priority),
+ itemId = insertItemRank(priority),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index ded5581..d1bbe57 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -62,8 +62,12 @@
/** Delete a widget by id from app widget service and the database. */
fun deleteWidget(widgetId: Int) {}
- /** Update the order of widgets in the database. */
- fun updateWidgetOrder(ids: List<Int>) {}
+ /**
+ * Update the order of widgets in the database.
+ *
+ * @param widgetIdToPriorityMap mapping of the widget ids to the priority of the widget.
+ */
+ fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {}
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -168,11 +172,11 @@
}
}
- override fun updateWidgetOrder(ids: List<Int>) {
+ override fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {
applicationScope.launch(bgDispatcher) {
- communalWidgetDao.updateWidgetOrder(ids)
+ communalWidgetDao.updateWidgetOrder(widgetIdToPriorityMap)
logger.i({ "Updated the order of widget list with ids: $str1." }) {
- str1 = ids.toString()
+ str1 = widgetIdToPriorityMap.toString()
}
}
}
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 e342c6b..0f4e583 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
@@ -102,8 +102,13 @@
/** Delete a widget by id. */
fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id)
- /** Reorder widgets. The order in the list will be their display order in the hub. */
- fun updateWidgetOrder(ids: List<Int>) = widgetRepository.updateWidgetOrder(ids)
+ /**
+ * Reorder the widgets.
+ *
+ * @param widgetIdToPriorityMap mapping of the widget ids to their new priorities.
+ */
+ fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) =
+ widgetRepository.updateWidgetOrder(widgetIdToPriorityMap)
/** A list of widget content to be displayed in the communal hub. */
val widgetContent: Flow<List<CommunalContentModel.Widget>> =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index bb9b4b5..3ae5229 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -20,6 +20,7 @@
import android.appwidget.AppWidgetProviderInfo
import android.widget.RemoteViews
import com.android.systemui.communal.shared.model.CommunalContentSize
+import java.util.UUID
/** Encapsulates data for a communal content. */
sealed interface CommunalContentModel {
@@ -39,6 +40,13 @@
override val size = CommunalContentSize.HALF
}
+ /** A placeholder item representing a new widget being added */
+ class WidgetPlaceholder : CommunalContentModel {
+ override val key: String = "widget_placeholder_${UUID.randomUUID()}"
+ // Same as widget size.
+ override val size = CommunalContentSize.HALF
+ }
+
class Tutorial(
id: Int,
override val size: CommunalContentSize,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 708f137..577e404 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.ui.viewmodel
+import android.content.ComponentName
import android.os.PowerManager
import android.os.SystemClock
import android.view.MotionEvent
@@ -53,6 +54,13 @@
communalInteractor.setTransitionState(transitionState)
}
+ /**
+ * Called when a widget is added via drag and drop from the widget picker into the communal hub.
+ */
+ fun onAddWidget(componentName: ComponentName, priority: Int) {
+ communalInteractor.addWidget(componentName, priority)
+ }
+
// TODO(b/308813166): remove once CommunalContainer is moved lower in z-order and doesn't block
// touches anymore.
/** Called when a touch is received outside the edge swipe area when hub mode is closed. */
@@ -82,8 +90,14 @@
/** Called as the UI requests deleting a widget. */
open fun onDeleteWidget(id: Int) {}
- /** Called as the UI requests reordering widgets. */
- open fun onReorderWidgets(ids: List<Int>) {}
+ /**
+ * Called as the UI requests reordering widgets.
+ *
+ * @param widgetIdToPriorityMap mapping of the widget ids to its priority. When re-ordering to
+ * add a new item in the middle, provide the priorities of existing widgets as if the new item
+ * existed, and then, call [onAddWidget] to add the new item at intended order.
+ */
+ open fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) {}
/** Called as the UI requests opening the widget editor. */
open fun onOpenWidgetEditor() {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index c82e000..368df9e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -47,5 +47,6 @@
override fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
- override fun onReorderWidgets(ids: List<Int>) = communalInteractor.updateWidgetOrder(ids)
+ override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
+ communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 887b18c..c936c63 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -16,8 +16,9 @@
package com.android.systemui.communal.widgets
-import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
import android.content.Intent
+import android.content.pm.PackageManager
import android.os.Bundle
import android.os.RemoteException
import android.util.Log
@@ -39,10 +40,9 @@
private var windowManagerService: IWindowManager? = null,
) : ComponentActivity() {
companion object {
- /**
- * Intent extra name for the {@link AppWidgetProviderInfo} of a widget to add to hub mode.
- */
- const val ADD_WIDGET_INFO = "add_widget_info"
+ private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag"
+ private const val EXTRA_FILTER_STRATEGY = "filter_strategy"
+ private const val FILTER_STRATEGY_GLANCEABLE_HUB = 1
private const val TAG = "EditWidgetsActivity"
}
@@ -50,15 +50,23 @@
registerForActivityResult(StartActivityForResult()) { result ->
when (result.resultCode) {
RESULT_OK -> {
- result.data
- ?.let {
- it.getParcelableExtra(
- ADD_WIDGET_INFO,
- AppWidgetProviderInfo::class.java
- )
+ result.data?.let { intent ->
+ val isPendingWidgetDrag =
+ intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false)
+ // Nothing to do when a widget is being dragged & dropped. The drop
+ // target in the communal grid will receive the widget to be added (if
+ // the user drops it over).
+ if (!isPendingWidgetDrag) {
+ intent
+ .getParcelableExtra(
+ Intent.EXTRA_COMPONENT_NAME,
+ ComponentName::class.java
+ )
+ ?.let { communalInteractor.addWidget(it, 0) }
+ ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
}
- ?.let { communalInteractor.addWidget(it.provider, 0) }
- ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+ }
+ ?: run { Log.w(TAG, "No data in result.") }
}
else ->
Log.w(
@@ -75,9 +83,29 @@
activity = this,
viewModel = communalViewModel,
onOpenWidgetPicker = {
- addWidgetActivityLauncher.launch(
- Intent(applicationContext, WidgetPickerActivity::class.java)
- )
+ val localPackageManager: PackageManager = getPackageManager()
+ val intent =
+ Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) }
+ localPackageManager
+ .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ ?.activityInfo
+ ?.packageName
+ ?.let { packageName ->
+ try {
+ addWidgetActivityLauncher.launch(
+ Intent(Intent.ACTION_PICK).also {
+ it.setPackage(packageName)
+ it.putExtra(
+ EXTRA_FILTER_STRATEGY,
+ FILTER_STRATEGY_GLANCEABLE_HUB
+ )
+ }
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to launch widget picker activity", e)
+ }
+ }
+ ?: run { Log.e(TAG, "Couldn't resolve launcher package name") }
},
onEditDone = {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
deleted file mode 100644
index a26afc8..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
+++ /dev/null
@@ -1,104 +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.communal.widgets
-
-import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetProviderInfo
-import android.content.Intent
-import android.graphics.Color
-import android.os.Bundle
-import android.util.Log
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.LinearLayout
-import androidx.activity.ComponentActivity
-import androidx.core.view.setMargins
-import androidx.core.view.setPadding
-import com.android.systemui.res.R
-import javax.inject.Inject
-
-/**
- * An Activity responsible for displaying a list of widgets to add to the hub mode grid. This is
- * essentially a placeholder until Launcher's widget picker can be used.
- */
-class WidgetPickerActivity
-@Inject
-constructor(
- private val appWidgetManager: AppWidgetManager,
-) : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContentView(R.layout.widget_picker)
- loadWidgets()
- }
-
- private fun loadWidgets() {
- val containerView: ViewGroup? = findViewById(R.id.widgets_container)
- containerView?.apply {
- try {
- appWidgetManager
- .getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
- ?.stream()
- ?.forEach { widgetInfo ->
- val activity = this@WidgetPickerActivity
- (widgetInfo.loadPreviewImage(activity, 0)
- ?: widgetInfo.loadIcon(activity, 0))
- ?.let {
- addView(
- ImageView(activity).also { v ->
- v.setImageDrawable(it)
- v.setBackgroundColor(WIDGET_PREVIEW_BACKGROUND_COLOR)
- v.setPadding(WIDGET_PREVIEW_PADDING)
- v.layoutParams =
- LinearLayout.LayoutParams(
- WIDGET_PREVIEW_SIZE,
- WIDGET_PREVIEW_SIZE
- )
- .also { lp ->
- lp.setMargins(WIDGET_PREVIEW_MARGINS)
- }
- v.setOnClickListener {
- setResult(
- RESULT_OK,
- Intent()
- .putExtra(
- EditWidgetsActivity.ADD_WIDGET_INFO,
- widgetInfo
- )
- )
- finish()
- }
- }
- )
- }
- }
- } catch (e: RuntimeException) {
- Log.e(TAG, "Exception fetching widget providers", e)
- }
- }
- }
-
- companion object {
- private const val WIDGET_PREVIEW_SIZE = 600
- private const val WIDGET_PREVIEW_MARGINS = 32
- private const val WIDGET_PREVIEW_PADDING = 32
- private val WIDGET_PREVIEW_BACKGROUND_COLOR = Color.rgb(216, 225, 220)
- private const val TAG = "WidgetPickerActivity"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 4b27af1..9afd5ed 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,7 +20,6 @@
import com.android.systemui.ForegroundServicesDialog;
import com.android.systemui.communal.widgets.EditWidgetsActivity;
-import com.android.systemui.communal.widgets.WidgetPickerActivity;
import com.android.systemui.contrast.ContrastDialogActivity;
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
@@ -158,12 +157,6 @@
@ClassKey(EditWidgetsActivity.class)
public abstract Activity bindEditWidgetsActivity(EditWidgetsActivity activity);
- /** Inject into WidgetPickerActivity. */
- @Binds
- @IntoMap
- @ClassKey(WidgetPickerActivity.class)
- public abstract Activity bindWidgetPickerActivity(WidgetPickerActivity activity);
-
/** Inject into SwitchToManagedProfileForCallActivity. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 9e8c0ec..e78ce3b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -17,8 +17,12 @@
package com.android.systemui.dagger;
import android.content.Context;
+import android.os.Looper;
import com.android.systemui.dagger.qualifiers.InstrumentationTest;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.SystemPropertiesHelper;
+import com.android.systemui.process.ProcessWrapper;
import com.android.systemui.util.InitializationChecker;
import dagger.BindsInstance;
@@ -58,4 +62,20 @@
* Returns an {@link InitializationChecker}.
*/
InitializationChecker getInitializationChecker();
+
+ /**
+ * Returns the main looper for this process.
+ */
+ @Main
+ Looper getMainLooper();
+
+ /**
+ * Returns a {@link SystemPropertiesHelper}.
+ */
+ SystemPropertiesHelper getSystemPropertiesHelper();
+
+ /**
+ * Returns a {@link ProcessWrapper}
+ */
+ ProcessWrapper getProcessWrapper();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 236c5b8..50f861f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -23,6 +23,8 @@
import android.hardware.SensorPrivacyManager;
import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.ScreenDecorationsModule;
+import com.android.systemui.accessibility.SystemActionsModule;
import com.android.systemui.battery.BatterySaverModule;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -34,6 +36,7 @@
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
+import com.android.systemui.reardisplay.RearDisplayModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
import com.android.systemui.rotationlock.RotationLockModule;
@@ -59,6 +62,7 @@
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
+import com.android.systemui.toast.ToastModule;
import com.android.systemui.volume.dagger.VolumeModule;
import com.android.systemui.wallpapers.dagger.WallpaperModule;
@@ -89,19 +93,23 @@
CollapsedStatusBarFragmentStartableModule.class,
GestureModule.class,
HeadsUpModule.class,
+ KeyboardShortcutsModule.class,
MediaModule.class,
MultiUserUtilsModule.class,
NavigationBarControllerModule.class,
PowerModule.class,
QSModule.class,
- ShadeModule.class,
+ RearDisplayModule.class,
ReferenceScreenshotModule.class,
RotationLockModule.class,
- SceneContainerFrameworkModule.class,
+ ScreenDecorationsModule.class,
+ SystemActionsModule.class,
+ ShadeModule.class,
StartCentralSurfacesModule.class,
+ SceneContainerFrameworkModule.class,
+ ToastModule.class,
VolumeModule.class,
- WallpaperModule.class,
- KeyboardShortcutsModule.class
+ WallpaperModule.class
})
public abstract class ReferenceSystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index d041acb..ac71664 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -19,12 +19,9 @@
import com.android.keyguard.KeyguardBiometricLockoutLogger
import com.android.systemui.CoreStartable
import com.android.systemui.LatencyTester
-import com.android.systemui.ScreenDecorations
import com.android.systemui.SliceBroadcastRelayHandler
-import com.android.systemui.accessibility.SystemActions
import com.android.systemui.accessibility.Magnification
import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.BiometricNotificationService
import com.android.systemui.clipboardoverlay.ClipboardListener
import com.android.systemui.controls.dagger.StartControlsStartableModule
@@ -46,10 +43,6 @@
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderCoordinator
import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherCoreStartable
-import com.android.systemui.power.PowerUI
-import com.android.systemui.reardisplay.RearDisplayDialogController
-import com.android.systemui.recents.Recents
-import com.android.systemui.recents.ScreenPinningRequest
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.ImmersiveModeConfirmation
@@ -61,11 +54,9 @@
import com.android.systemui.stylus.StylusUsiPowerStartable
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.theme.ThemeOverlayController
-import com.android.systemui.toast.ToastUI
import com.android.systemui.usb.StorageNotification
import com.android.systemui.util.NotificationChannels
import com.android.systemui.util.StartBinderLoggerModule
-import com.android.systemui.volume.VolumeUI
import com.android.systemui.wallpapers.dagger.WallpaperModule
import com.android.systemui.wmshell.WMShell
import dagger.Binds
@@ -74,7 +65,12 @@
import dagger.multibindings.IntoMap
/**
- * Collection of {@link CoreStartable}s that should be run on AOSP.
+ * DEPRECATED: DO NOT ADD THINGS TO THIS FILE.
+ *
+ * Add a feature specific daggger module for what you are working on. Bind your CoreStartable there.
+ * Include that module where it is needed.
+ *
+ * @deprecated
*/
@Module(
includes = [
@@ -85,12 +81,6 @@
]
)
abstract class SystemUICoreStartableModule {
- /** Inject into AuthController. */
- @Binds
- @IntoMap
- @ClassKey(AuthController::class)
- abstract fun bindAuthController(service: AuthController): CoreStartable
-
/** Inject into BiometricNotificationService */
@Binds
@IntoMap
@@ -158,18 +148,6 @@
@PerUser
abstract fun bindNotificationChannels(sysui: NotificationChannels): CoreStartable
- /** Inject into PowerUI. */
- @Binds
- @IntoMap
- @ClassKey(PowerUI::class)
- abstract fun bindPowerUI(sysui: PowerUI): CoreStartable
-
- /** Inject into Recents. */
- @Binds
- @IntoMap
- @ClassKey(Recents::class)
- abstract fun bindRecents(sysui: Recents): CoreStartable
-
/** Inject into ImmersiveModeConfirmation. */
@Binds
@IntoMap
@@ -182,12 +160,6 @@
@ClassKey(RingtonePlayer::class)
abstract fun bind(sysui: RingtonePlayer): CoreStartable
- /** Inject into ScreenDecorations. */
- @Binds
- @IntoMap
- @ClassKey(ScreenDecorations::class)
- abstract fun bindScreenDecorations(sysui: ScreenDecorations): CoreStartable
-
/** Inject into GesturePointerEventHandler. */
@Binds
@IntoMap
@@ -218,23 +190,12 @@
@ClassKey(StorageNotification::class)
abstract fun bindStorageNotification(sysui: StorageNotification): CoreStartable
- /** Inject into SystemActions. */
- @Binds
- @IntoMap
- @ClassKey(SystemActions::class)
- abstract fun bindSystemActions(sysui: SystemActions): CoreStartable
-
/** Inject into ThemeOverlayController. */
@Binds
@IntoMap
@ClassKey(ThemeOverlayController::class)
abstract fun bindThemeOverlayController(sysui: ThemeOverlayController): CoreStartable
- /** Inject into ToastUI. */
- @Binds
- @IntoMap
- @ClassKey(ToastUI::class)
- abstract fun bindToastUI(service: ToastUI): CoreStartable
/** Inject into MediaOutputSwitcherDialogUI. */
@Binds
@@ -242,12 +203,6 @@
@ClassKey(MediaOutputSwitcherDialogUI::class)
abstract fun MediaOutputSwitcherDialogUI(sysui: MediaOutputSwitcherDialogUI): CoreStartable
- /** Inject into VolumeUI. */
- @Binds
- @IntoMap
- @ClassKey(VolumeUI::class)
- abstract fun bindVolumeUI(sysui: VolumeUI): CoreStartable
-
/** Inject into Magnification. */
@Binds
@IntoMap
@@ -293,11 +248,6 @@
abstract fun bindChipbarController(sysui: ChipbarCoordinator): CoreStartable
- /** Inject into RearDisplayDialogController) */
- @Binds
- @IntoMap
- @ClassKey(RearDisplayDialogController::class)
- abstract fun bindRearDisplayDialogController(sysui: RearDisplayDialogController): CoreStartable
/** Inject into StylusUsiPowerStartable) */
@Binds
@@ -361,9 +311,4 @@
@IntoMap
@ClassKey(KeyguardDismissBinder::class)
abstract fun bindKeyguardDismissBinder(impl: KeyguardDismissBinder): CoreStartable
-
- @Binds
- @IntoMap
- @ClassKey(ScreenPinningRequest::class)
- abstract fun bindScreenPinningRequest(impl: ScreenPinningRequest): CoreStartable
}
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 47be8ab..f6a9570 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
@@ -159,7 +159,7 @@
fun attemptDeviceEntry() {
// 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
+ // 2. Show SPFS/UDFPS bouncer if it is available AlternateBouncerInteractor.show
// 3. For face auth only setups trigger face auth, delay transitioning to bouncer for
// a small amount of time.
// 4. Transition to bouncer scene
@@ -197,8 +197,8 @@
init {
if (flags.isEnabled()) {
applicationScope.launch {
- authenticationInteractor.authenticationChallengeResult.collectLatest { successful ->
- if (successful) {
+ authenticationInteractor.onAuthenticationResult.collectLatest { isSuccessful ->
+ if (isSuccessful) {
repository.reportSuccessfulAuthentication()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt
new file mode 100644
index 0000000..ef2537c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.deviceentry.ui.binder
+
+import android.annotation.SuppressLint
+import androidx.core.view.isInvisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
+import com.android.systemui.deviceentry.ui.viewmodel.UdfpsAccessibilityOverlayViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+object UdfpsAccessibilityOverlayBinder {
+
+ /** Forwards hover events to the view model to make guided announcements for accessibility. */
+ @SuppressLint("ClickableViewAccessibility")
+ @JvmStatic
+ fun bind(
+ view: UdfpsAccessibilityOverlay,
+ viewModel: UdfpsAccessibilityOverlayViewModel,
+ ) {
+ view.setOnHoverListener { v, event -> viewModel.onHoverEvent(v, event) }
+ view.repeatWhenAttached {
+ // Repeat on CREATED because we update the visibility of the view
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ viewModel.visible.collect { visible -> view.isInvisible = !visible }
+ }
+ }
+ }
+}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
similarity index 71%
copy from core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
copy to packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
index 6c1f0fc..7be3230 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package android.companion.virtual.camera;
+package com.android.systemui.deviceentry.ui.view
-/**
- * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
- * VirtualCamera.
- * @hide
- */
-parcelable VirtualCameraMetadata;
+import android.content.Context
+import android.view.View
+
+/** Overlay to handle under-fingerprint sensor accessibility events. */
+class UdfpsAccessibilityOverlay(context: Context?) : View(context)
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt
new file mode 100644
index 0000000..eeac527
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.deviceentry.ui.viewmodel
+
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Models the UI state for the alternate bouncer UDFPS accessibility overlay */
+@ExperimentalCoroutinesApi
+class AlternateBouncerUdfpsAccessibilityOverlayViewModel
+@Inject
+constructor(
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ accessibilityInteractor: AccessibilityInteractor,
+) :
+ UdfpsAccessibilityOverlayViewModel(
+ udfpsOverlayInteractor,
+ accessibilityInteractor,
+ ) {
+ /** Overlay is always visible if touch exploration is enabled on the alternate bouncer. */
+ override fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt
new file mode 100644
index 0000000..af51576
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/DeviceEntryUdfpsAccessibilityOverlayViewModel.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.deviceentry.ui.viewmodel
+
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/** Models the UI state for the non-alternate bouncer UDFPS accessibility overlay */
+@ExperimentalCoroutinesApi
+class DeviceEntryUdfpsAccessibilityOverlayViewModel
+@Inject
+constructor(
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ accessibilityInteractor: AccessibilityInteractor,
+ private val deviceEntryIconViewModel: DeviceEntryIconViewModel,
+ private val deviceEntryFgIconViewModel: DeviceEntryForegroundViewModel,
+) :
+ UdfpsAccessibilityOverlayViewModel(
+ udfpsOverlayInteractor,
+ accessibilityInteractor,
+ ) {
+ /** Overlay is only visible if the UDFPS icon is visible on the keyguard. */
+ override fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean> =
+ combine(
+ deviceEntryFgIconViewModel.viewModel,
+ deviceEntryIconViewModel.deviceEntryViewAlpha,
+ ) { iconViewModel, alpha ->
+ iconViewModel.type == DeviceEntryIconView.IconType.FINGERPRINT &&
+ !iconViewModel.useAodVariant &&
+ alpha == 1f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
new file mode 100644
index 0000000..f5a8870
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.deviceentry.ui.viewmodel
+
+import android.graphics.Point
+import android.view.MotionEvent
+import android.view.View
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.biometrics.UdfpsUtils
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+/** Models the UI state for the UDFPS accessibility overlay */
+@ExperimentalCoroutinesApi
+abstract class UdfpsAccessibilityOverlayViewModel(
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ accessibilityInteractor: AccessibilityInteractor,
+) {
+ private val udfpsUtils = UdfpsUtils()
+ private val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
+ udfpsOverlayInteractor.udfpsOverlayParams
+
+ val visible: Flow<Boolean> =
+ accessibilityInteractor.isTouchExplorationEnabled.flatMapLatest { touchExplorationEnabled ->
+ if (touchExplorationEnabled) {
+ isVisibleWhenTouchExplorationEnabled()
+ } else {
+ flowOf(false)
+ }
+ }
+
+ abstract fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean>
+
+ /** Give directional feedback to help the user authenticate with UDFPS. */
+ fun onHoverEvent(v: View, event: MotionEvent): Boolean {
+ val overlayParams = udfpsOverlayParams.value
+ val scaledTouch: Point =
+ udfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0), event, overlayParams)
+
+ if (!udfpsUtils.isWithinSensorArea(event.getPointerId(0), event, overlayParams)) {
+ // view only receives motionEvents when [visible] which requires touchExplorationEnabled
+ val announceStr =
+ udfpsUtils.onTouchOutsideOfSensorArea(
+ /* touchExplorationEnabled */ true,
+ v.context,
+ scaledTouch.x,
+ scaledTouch.y,
+ overlayParams,
+ )
+ if (announceStr != null) {
+ v.announceForAccessibility(announceStr)
+ }
+ }
+ // always let the motion events go through to underlying views
+ return false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
index 6560ee3..14fda5e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
@@ -73,9 +73,19 @@
) {
val isMet = !alphaEnabled || betaEnabled
override fun toString(): String {
- val isMetBullet = if (isMet) "+" else "-"
- return "$isMetBullet $alphaName ($alphaEnabled) DEPENDS ON $betaName ($betaEnabled)"
+ val prefix =
+ when {
+ !isMet -> " [NOT MET]"
+ alphaEnabled -> " [met]"
+ betaEnabled -> " [ready]"
+ else -> "[not ready]"
+ }
+ val alphaState = if (alphaEnabled) "enabled" else "disabled"
+ val betaState = if (betaEnabled) "enabled" else "disabled"
+ return "$prefix $alphaName ($alphaState) DEPENDS ON $betaName ($betaState)"
}
+ /** Used whe posting a notification of unmet dependencies */
+ fun shortUnmetString(): String = "$alphaName DEPENDS ON $betaName"
}
protected infix fun UnreleasedFlag.dependsOn(other: UnreleasedFlag) =
@@ -124,7 +134,7 @@
unmet: List<FlagDependenciesBase.Dependency>
) {
val title = "Invalid flag dependencies: ${unmet.size} of ${all.size}"
- val details = unmet.joinToString("\n")
+ val details = unmet.joinToString("\n") { it.shortUnmetString() }
Log.e("FlagDependencies", "$title:\n$details")
val channel = NotificationChannel("FLAGS", "Flags", NotificationManager.IMPORTANCE_DEFAULT)
val notification =
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b43f54d..bb0c273 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -17,11 +17,11 @@
import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
-import com.android.systemui.res.R
import com.android.systemui.flags.FlagsFactory.releasedFlag
import com.android.systemui.flags.FlagsFactory.resourceBooleanFlag
import com.android.systemui.flags.FlagsFactory.sysPropBooleanFlag
import com.android.systemui.flags.FlagsFactory.unreleasedFlag
+import com.android.systemui.res.R
/**
* List of [Flag] objects for use in SystemUI.
@@ -208,10 +208,6 @@
val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
unreleasedFlag("wallpaper_picker_grid_apply_button")
- /** Provide new auth messages on the bouncer. */
- // TODO(b/277961132): Tracking bug.
- @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
-
/** Keyguard Migration */
// TODO(b/297037052): Tracking bug.
@@ -483,9 +479,6 @@
// TODO(b/264916608): Tracking Bug
@JvmField val SCREENSHOT_METADATA = unreleasedFlag("screenshot_metadata")
- // TODO(b/266955521): Tracking bug
- @JvmField val SCREENSHOT_DETECTION = releasedFlag("screenshot_detection")
-
// TODO(b/251205791): Tracking Bug
@JvmField val SCREENSHOT_APP_CLIPS = releasedFlag("screenshot_app_clips")
@@ -588,10 +581,6 @@
@JvmField
val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION = unreleasedFlag("split_shade_subpixel_optimization")
- // TODO(b/288868056): Tracking Bug
- @JvmField
- val PARTIAL_SCREEN_SHARING_TASK_SWITCHER = unreleasedFlag("pss_task_switcher")
-
// TODO(b/278761837): Tracking Bug
@JvmField val USE_NEW_ACTIVITY_STARTER = releasedFlag(name = "use_new_activity_starter")
@@ -607,19 +596,11 @@
@JvmField
val LOCKSCREEN_WALLPAPER_DREAM_ENABLED = unreleasedFlag("enable_lockscreen_wallpaper_dream")
- // TODO(b/283084712): Tracking Bug
- @JvmField val IMPROVED_HUN_ANIMATIONS = unreleasedFlag("improved_hun_animations")
-
// TODO(b/283447257): Tracking bug
@JvmField
val BIGPICTURE_NOTIFICATION_LAZY_LOADING =
unreleasedFlag("bigpicture_notification_lazy_loading")
- // TODO(b/292062937): Tracking bug
- @JvmField
- val NOTIFICATION_CLEARABLE_REFACTOR =
- unreleasedFlag("notification_clearable_refactor")
-
// TODO(b/283740863): Tracking Bug
@JvmField
val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
index 6c16097..6fa20de 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
@@ -17,19 +17,22 @@
package com.android.systemui.flags
import android.os.SystemProperties
-import com.android.systemui.dagger.SysUISingleton
-
import javax.inject.Inject
+import javax.inject.Singleton
/**
* Proxy to make {@link SystemProperties} easily testable.
*/
-@SysUISingleton
+@Singleton
open class SystemPropertiesHelper @Inject constructor() {
fun get(name: String): String {
return SystemProperties.get(name)
}
+ fun get(name: String, def: String?): String {
+ return SystemProperties.get(name, def)
+ }
+
fun getBoolean(name: String, default: Boolean): Boolean {
return SystemProperties.getBoolean(name, default)
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
index 629b361..cfa5294 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
@@ -65,4 +65,11 @@
SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, previousEvent.currentProgress)
}
}
+
+ /** The arrow navigation that was operating the slider has stopped. */
+ fun onArrowUp() {
+ _currentEvent.update { previousEvent ->
+ SliderEvent(SliderEventType.ARROW_UP, previousEvent.currentProgress)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
index d89cf63..10098fa 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderTracker.kt
@@ -58,7 +58,7 @@
override suspend fun iterateState(event: SliderEvent) {
when (currentState) {
- SliderState.IDLE -> handleIdle(event.type)
+ SliderState.IDLE -> handleIdle(event.type, event.currentProgress)
SliderState.WAIT -> handleWait(event.type, event.currentProgress)
SliderState.DRAG_HANDLE_ACQUIRED_BY_TOUCH -> handleAcquired(event.type)
SliderState.DRAG_HANDLE_DRAGGING -> handleDragging(event.type, event.currentProgress)
@@ -67,17 +67,26 @@
SliderState.DRAG_HANDLE_RELEASED_FROM_TOUCH -> setState(SliderState.IDLE)
SliderState.JUMP_TRACK_LOCATION_SELECTED -> handleJumpToTrack(event.type)
SliderState.JUMP_BOOKEND_SELECTED -> handleJumpToBookend(event.type)
+ SliderState.ARROW_HANDLE_MOVED_ONCE -> handleArrowOnce(event.type)
+ SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY ->
+ handleArrowContinuous(event.type, event.currentProgress)
+ SliderState.ARROW_HANDLE_REACHED_BOOKEND -> handleArrowBookend()
}
latestProgress = event.currentProgress
}
- private fun handleIdle(newEventType: SliderEventType) {
+ private fun handleIdle(newEventType: SliderEventType, currentProgress: Float) {
if (newEventType == SliderEventType.STARTED_TRACKING_TOUCH) {
timerJob = launchTimer()
// The WAIT state will wait for the timer to complete or a slider progress to occur.
// This will disambiguate between an imprecise touch that acquires the slider handle,
// and a select and jump operation in the slider track.
setState(SliderState.WAIT)
+ } else if (newEventType == SliderEventType.PROGRESS_CHANGE_BY_PROGRAM) {
+ val state =
+ if (bookendReached(currentProgress)) SliderState.ARROW_HANDLE_REACHED_BOOKEND
+ else SliderState.ARROW_HANDLE_MOVED_ONCE
+ setState(state)
}
}
@@ -176,6 +185,13 @@
SliderState.DRAG_HANDLE_REACHED_BOOKEND -> executeOnBookend()
SliderState.JUMP_TRACK_LOCATION_SELECTED ->
sliderListener.onProgressJump(latestProgress)
+ SliderState.ARROW_HANDLE_MOVED_ONCE -> sliderListener.onSelectAndArrow(latestProgress)
+ SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY -> sliderListener.onProgress(latestProgress)
+ SliderState.ARROW_HANDLE_REACHED_BOOKEND -> {
+ executeOnBookend()
+ // This transitory execution must also reset the state
+ resetState()
+ }
else -> {}
}
}
@@ -204,6 +220,43 @@
currentProgress <= config.lowerBookendThreshold
}
+ private fun handleArrowOnce(newEventType: SliderEventType) {
+ val nextState =
+ when (newEventType) {
+ SliderEventType.STARTED_TRACKING_TOUCH -> {
+ // Launching the timer and going to WAIT
+ timerJob = launchTimer()
+ SliderState.WAIT
+ }
+ SliderEventType.PROGRESS_CHANGE_BY_PROGRAM ->
+ SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+ SliderEventType.ARROW_UP -> SliderState.IDLE
+ else -> SliderState.ARROW_HANDLE_MOVED_ONCE
+ }
+ setState(nextState)
+ }
+
+ private fun handleArrowContinuous(newEventType: SliderEventType, currentProgress: Float) {
+ val reachedBookend = bookendReached(currentProgress)
+ val nextState =
+ when (newEventType) {
+ SliderEventType.ARROW_UP -> SliderState.IDLE
+ SliderEventType.STARTED_TRACKING_TOUCH -> {
+ // Launching the timer and going to WAIT
+ timerJob = launchTimer()
+ SliderState.WAIT
+ }
+ SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> {
+ if (reachedBookend) SliderState.ARROW_HANDLE_REACHED_BOOKEND
+ else SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+ }
+ else -> SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY
+ }
+ setState(nextState)
+ }
+
+ private fun handleArrowBookend() = setState(SliderState.IDLE)
+
@VisibleForTesting
fun setState(state: SliderState) {
currentState = state
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
index 413e277..4a63941 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
@@ -29,5 +29,5 @@
/* The slider has stopped tracking touch events. */
STOPPED_TRACKING_TOUCH,
/* The external (not touch) stimulus that was modifying the slider progress has stopped. */
- EXTERNAL_STIMULUS_RELEASE,
+ ARROW_UP,
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
index fe092e6..de6ddd7 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderState.kt
@@ -32,6 +32,12 @@
DRAG_HANDLE_REACHED_BOOKEND,
/* A location in the slider track has been selected. */
JUMP_TRACK_LOCATION_SELECTED,
- /* The slider handled moved to a bookend after it was selected. */
+ /* The slider handle moved to a bookend after it was selected. */
JUMP_BOOKEND_SELECTED,
+ /** The slider handle moved due to single select-and-arrow operation */
+ ARROW_HANDLE_MOVED_ONCE,
+ /** The slider handle moves continuously due to constant select-and-arrow operations */
+ ARROW_HANDLE_MOVES_CONTINUOUSLY,
+ /** The slider handle reached a bookend due to a select-and-arrow operation */
+ ARROW_HANDLE_REACHED_BOOKEND,
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index c490ce7..342325f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -30,7 +30,7 @@
import android.os.Binder
import android.os.Bundle
import android.util.Log
-import com.android.app.tracing.TraceUtils.Companion.runBlocking
+import com.android.app.tracing.coroutines.runBlocking
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 1f69cc0..0d40511 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -397,8 +397,10 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_DATE_CHANGED);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
- null /* scheduler */);
+ mBgHandler.post(() -> {
+ getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
+ null /* scheduler */);
+ });
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mRegistered = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 20da00e..af5d48d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -124,7 +124,7 @@
indicationAreaHandle =
KeyguardIndicationAreaBinder.bind(
- notificationShadeWindowView,
+ notificationShadeWindowView.requireViewById(R.id.keyguard_indication_area),
keyguardIndicationAreaViewModel,
keyguardRootViewModel,
indicationController,
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 3925dd1..13e3835 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -34,6 +34,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.mediator.ScreenOnCoordinator;
+import com.android.systemui.CoreStartable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -79,9 +80,12 @@
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
+import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
import java.util.concurrent.Executor;
@@ -105,7 +109,7 @@
StartKeyguardTransitionModule.class,
ResourceTrimmerModule.class,
})
-public class KeyguardModule {
+public interface KeyguardModule {
/**
* Provides our instance of KeyguardViewMediator which is considered optional.
*/
@@ -207,13 +211,19 @@
/** */
@Provides
- public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
+ static ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
return viewMediator.getViewMediatorCallback();
}
/** */
@Provides
- public KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
+ static KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
return new KeyguardQuickAffordancesMetricsLoggerImpl();
}
+
+ /** Binds {@link KeyguardUpdateMonitor} as a {@link CoreStartable}. */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardUpdateMonitor.class)
+ CoreStartable bindsKeyguardUpdateMonitor(KeyguardUpdateMonitor keyguardUpdateMonitor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 4d60dd0..17d7836 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -626,17 +626,19 @@
faceAuthLogger.skippingDetection(_isAuthRunning.value, detectCancellationSignal != null)
return
}
- detectCancellationSignal?.cancel()
- detectCancellationSignal = CancellationSignal()
withContext(mainDispatcher) {
// We always want to invoke face detect in the main thread.
faceAuthLogger.faceDetectionStarted()
- faceManager?.detectFace(
- checkNotNull(detectCancellationSignal),
- detectionCallback,
- SysUiFaceAuthenticateOptions(currentUserId, uiEvent, uiEvent.extraInfo)
- .toFaceAuthenticateOptions()
- )
+ detectCancellationSignal?.cancel()
+ detectCancellationSignal = CancellationSignal()
+ detectCancellationSignal?.let {
+ faceManager?.detectFace(
+ it,
+ detectionCallback,
+ SysUiFaceAuthenticateOptions(currentUserId, uiEvent, uiEvent.extraInfo)
+ .toFaceAuthenticateOptions()
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index de15fd6..ecf78d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -22,15 +22,19 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.systemui.keyguard.data.repository.BiometricType
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.res.R
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
/**
* Encapsulates business logic for device entry events that impact the side fingerprint sensor
@@ -40,6 +44,7 @@
class DeviceEntrySideFpsOverlayInteractor
@Inject
constructor(
+ @Application private val applicationScope: CoroutineScope,
@Application private val context: Context,
deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
@@ -48,7 +53,15 @@
) {
init {
- alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
+ if (!DeviceEntryUdfpsRefactor.isEnabled) {
+ applicationScope.launch {
+ deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType ->
+ if (sensorType == BiometricType.SIDE_FINGERPRINT) {
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
+ }
+ }
+ }
+ }
}
private val showIndicatorForPrimaryBouncer: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 9fe5c3f..cecc653 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -18,7 +18,7 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
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 7882a97..3888345 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
@@ -22,7 +22,7 @@
import android.content.Context
import android.content.Intent
import android.util.Log
-import com.android.app.tracing.TraceUtils.Companion.withContext
+import com.android.app.tracing.coroutines.withContext
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 64ff3b0c..1277585 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -32,11 +32,8 @@
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
/**
* Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and
@@ -68,8 +65,6 @@
* in the range of [0, 1]. View animations should begin and end within a subset of this
* range. This function maps the [startTime] and [duration] into [0, 1], when this subset is
* valid.
- *
- * Will produce a [SharedFlow], so that identical animations can use the same value.
*/
fun sharedFlow(
duration: Duration,
@@ -80,7 +75,7 @@
onFinish: (() -> Float)? = null,
interpolator: Interpolator = LINEAR,
name: String? = null
- ): SharedFlow<Float> {
+ ): Flow<Float> {
if (!duration.isPositive()) {
throw IllegalArgumentException("duration must be a positive number: $duration")
}
@@ -137,7 +132,6 @@
value
}
.filterNotNull()
- .shareIn(scope, SharingStarted.WhileSubscribed())
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt
deleted file mode 100644
index ebf1beb..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt
+++ /dev/null
@@ -1,25 +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.keyguard.ui.adapter
-
-/**
- * Temporary adapter class while
- * [com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController] is being refactored
- * before [com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy] is removed.
- *
- * TODO (b/278719514): Delete once udfps keyguard view is fully refactored.
- */
-interface UdfpsKeyguardViewControllerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index d12d193..c749818 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
@ExperimentalCoroutinesApi
object AlternateBouncerUdfpsViewBinder {
@@ -71,10 +72,12 @@
bgView.visibility = View.VISIBLE
bgView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.bgViewModel.collect { bgViewModel ->
- bgView.alpha = bgViewModel.alpha
- bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+ launch {
+ viewModel.bgColor.collect { color ->
+ bgView.imageTintList = ColorStateList.valueOf(color)
+ }
}
+ launch { viewModel.bgAlpha.collect { alpha -> bgView.alpha = alpha } }
}
}
}
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 78906ac..b2a3549 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
@@ -17,21 +17,24 @@
package com.android.systemui.keyguard.ui.binder
import android.view.View
+import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.classifier.Classifier
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
+import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.scrim.ScrimView
-import com.android.systemui.statusbar.gesture.TapGestureDetector
+import dagger.Lazy
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -47,21 +50,24 @@
@JvmStatic
fun bind(
view: ConstraintLayout,
- viewModel: AlternateBouncerViewModel,
- falsingManager: FalsingManager,
- swipeUpAnywhereGestureHandler: SwipeUpAnywhereGestureHandler,
- tapGestureDetector: TapGestureDetector,
- alternateBouncerUdfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+ alternateBouncerDependencies: AlternateBouncerDependencies,
) {
if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
return
}
- optionallyAddUdfpsView(
+ optionallyAddUdfpsViews(
view = view,
- alternateBouncerUdfpsIconViewModel = alternateBouncerUdfpsIconViewModel,
+ udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel,
+ udfpsA11yOverlayViewModel =
+ alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel,
)
val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
+ val viewModel = alternateBouncerDependencies.viewModel
+ val swipeUpAnywhereGestureHandler =
+ alternateBouncerDependencies.swipeUpAnywhereGestureHandler
+ val falsingManager = alternateBouncerDependencies.falsingManager
+ val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector
view.repeatWhenAttached { alternateBouncerViewContainer ->
repeatOnLifecycle(Lifecycle.State.STARTED) {
scrim.viewAlpha = 0f
@@ -102,44 +108,79 @@
}
}
- private fun optionallyAddUdfpsView(
+ private fun optionallyAddUdfpsViews(
view: ConstraintLayout,
- alternateBouncerUdfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+ udfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+ udfpsA11yOverlayViewModel: Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
) {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
- alternateBouncerUdfpsIconViewModel.iconLocation.collect { iconLocation ->
- val viewId = R.id.alternate_bouncer_udfps_icon_view
- var udfpsView = view.getViewById(viewId)
+ udfpsIconViewModel.iconLocation.collect { iconLocation ->
+ // add UDFPS a11y overlay
+ val udfpsA11yOverlayViewId =
+ R.id.alternate_bouncer_udfps_accessibility_overlay
+ var udfpsA11yOverlay = view.getViewById(udfpsA11yOverlayViewId)
+ if (udfpsA11yOverlay == null) {
+ udfpsA11yOverlay =
+ UdfpsAccessibilityOverlay(view.context).apply {
+ id = udfpsA11yOverlayViewId
+ }
+ view.addView(udfpsA11yOverlay)
+ UdfpsAccessibilityOverlayBinder.bind(
+ udfpsA11yOverlay,
+ udfpsA11yOverlayViewModel.get(),
+ )
+ }
+
+ // add UDFPS icon view
+ val udfpsViewId = R.id.alternate_bouncer_udfps_icon_view
+ var udfpsView = view.getViewById(udfpsViewId)
if (udfpsView == null) {
udfpsView =
- DeviceEntryIconView(view.context, null).apply { id = viewId }
+ DeviceEntryIconView(view.context, null).apply {
+ id = udfpsViewId
+ contentDescription =
+ context.resources.getString(
+ R.string.accessibility_fingerprint_label
+ )
+ }
view.addView(udfpsView)
AlternateBouncerUdfpsViewBinder.bind(
udfpsView,
- alternateBouncerUdfpsIconViewModel,
+ udfpsIconViewModel,
)
}
val constraintSet = ConstraintSet().apply { clone(view) }
constraintSet.apply {
- constrainWidth(viewId, iconLocation.width)
- constrainHeight(viewId, iconLocation.height)
+ // udfpsView:
+ constrainWidth(udfpsViewId, iconLocation.width)
+ constrainHeight(udfpsViewId, iconLocation.height)
connect(
- viewId,
+ udfpsViewId,
ConstraintSet.TOP,
ConstraintSet.PARENT_ID,
ConstraintSet.TOP,
iconLocation.top,
)
connect(
- viewId,
+ udfpsViewId,
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
iconLocation.left
)
+
+ // udfpsA11yOverlayView:
+ constrainWidth(
+ udfpsA11yOverlayViewId,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ constrainHeight(
+ udfpsA11yOverlayViewId,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
}
constraintSet.applyTo(view)
}
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 dcf4284..a02e8ac 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
@@ -131,10 +131,10 @@
bgView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch { bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha } }
launch {
- bgViewModel.viewModel.collect { bgViewModel ->
- bgView.alpha = bgViewModel.alpha
- bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+ bgViewModel.color.collect { color ->
+ bgView.imageTintList = ColorStateList.valueOf(color)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index eee5206..96e83b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -241,7 +241,6 @@
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Activated)
settingsMenu.setOnTouchListener(
KeyguardSettingsButtonOnTouchListener(
- view = settingsMenu,
viewModel = viewModel.settingsMenuViewModel,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 7d290c3..05fe0b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -32,8 +32,6 @@
import com.android.systemui.res.R
import kotlinx.coroutines.launch
-private val TAG = KeyguardClockViewBinder::class.simpleName
-
object KeyguardClockViewBinder {
@JvmStatic
fun bind(
@@ -74,12 +72,6 @@
applyConstraints(clockSection, keyguardRootView, true)
}
}
- launch {
- if (!migrateClocksToBlueprint()) return@launch
- viewModel.hasCustomWeatherDataDisplay.collect {
- applyConstraints(clockSection, keyguardRootView, true)
- }
- }
}
}
}
@@ -132,7 +124,7 @@
fun applyConstraints(
clockSection: ClockSection,
rootView: ConstraintLayout,
- animated: Boolean
+ animated: Boolean,
) {
val constraintSet = ConstraintSet().apply { clone(rootView) }
clockSection.applyConstraints(constraintSet)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 4efd9ef..4c33d90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -54,12 +54,11 @@
keyguardRootViewModel: KeyguardRootViewModel,
indicationController: KeyguardIndicationController,
): DisposableHandle {
- val indicationArea: ViewGroup = view.requireViewById(R.id.keyguard_indication_area)
- indicationController.setIndicationArea(indicationArea)
+ indicationController.setIndicationArea(view)
- val indicationText: TextView = indicationArea.requireViewById(R.id.keyguard_indication_text)
+ val indicationText: TextView = view.requireViewById(R.id.keyguard_indication_text)
val indicationTextBottom: TextView =
- indicationArea.requireViewById(R.id.keyguard_indication_text_bottom)
+ view.requireViewById(R.id.keyguard_indication_text_bottom)
view.clipChildren = false
view.clipToPadding = false
@@ -71,7 +70,7 @@
launch {
if (keyguardBottomAreaRefactor()) {
keyguardRootViewModel.alpha.collect { alpha ->
- indicationArea.apply {
+ view.apply {
this.importantForAccessibility =
if (alpha == 0f) {
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
@@ -83,7 +82,7 @@
}
} else {
viewModel.alpha.collect { alpha ->
- indicationArea.apply {
+ view.apply {
this.importantForAccessibility =
if (alpha == 0f) {
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
@@ -98,7 +97,7 @@
launch {
viewModel.indicationAreaTranslationX.collect { translationX ->
- indicationArea.translationX = translationX
+ view.translationX = translationX
}
}
@@ -113,9 +112,7 @@
0
}
}
- .collect { paddingPx ->
- indicationArea.setPadding(paddingPx, 0, paddingPx, 0)
- }
+ .collect { paddingPx -> view.setPadding(paddingPx, 0, paddingPx, 0) }
}
launch {
@@ -124,7 +121,7 @@
.flatMapLatest { defaultBurnInOffsetY ->
viewModel.indicationAreaTranslationY(defaultBurnInOffsetY)
}
- .collect { translationY -> indicationArea.translationY = translationY }
+ .collect { translationY -> view.translationY = translationY }
}
launch {
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 01a1ca3..fad0370 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
@@ -29,7 +29,6 @@
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.internal.jank.InteractionJankMonitor
@@ -67,6 +66,7 @@
import javax.inject.Provider
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -205,7 +205,6 @@
childViews[aodNotificationIconContainerId]
?.setAodNotifIconContainerIsVisible(
isVisible,
- featureFlags,
iconsAppearTranslationPx.value,
screenOffAnimationController,
)
@@ -256,6 +255,7 @@
vibratorHelper.performHapticFeedback(
view,
HapticFeedbackConstants.CONFIRM,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
)
}
}
@@ -265,6 +265,7 @@
vibratorHelper.performHapticFeedback(
view,
HapticFeedbackConstants.REJECT,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
)
}
}
@@ -359,41 +360,32 @@
}
}
- @JvmStatic
- fun bindAodIconVisibility(
+ suspend fun bindAodNotifIconVisibility(
view: View,
isVisible: Flow<AnimatedValue<Boolean>>,
configuration: ConfigurationState,
- featureFlags: FeatureFlagsClassic,
screenOffAnimationController: ScreenOffAnimationController,
- ): DisposableHandle? {
+ ) {
KeyguardShadeMigrationNssl.assertInLegacyMode()
- if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return null
- return view.repeatWhenAttached {
- lifecycleScope.launch {
- val iconAppearTranslationPx =
- configuration
- .getDimensionPixelSize(R.dimen.shelf_appear_translation)
- .stateIn(this)
- isVisible.collect { isVisible ->
- view.setAodNotifIconContainerIsVisible(
- isVisible,
- featureFlags,
- iconAppearTranslationPx.value,
- screenOffAnimationController,
- )
- }
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return
+ coroutineScope {
+ val iconAppearTranslationPx =
+ configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
+ isVisible.collect { isVisible ->
+ view.setAodNotifIconContainerIsVisible(
+ isVisible = isVisible,
+ iconsAppearTranslationPx = iconAppearTranslationPx.value,
+ screenOffAnimationController = screenOffAnimationController,
+ )
}
}
}
private fun View.setAodNotifIconContainerIsVisible(
isVisible: AnimatedValue<Boolean>,
- featureFlags: FeatureFlagsClassic,
iconsAppearTranslationPx: Int,
screenOffAnimationController: ScreenOffAnimationController,
) {
- val statusViewMigrated = KeyguardShadeMigrationNssl.isEnabled
animate().cancel()
val animatorListener =
object : AnimatorListenerAdapter() {
@@ -404,13 +396,13 @@
when {
!isVisible.isAnimating -> {
alpha = 1f
- if (!statusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
translationY = 0f
}
visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
}
newAodTransition() -> {
- animateInIconTranslation(statusViewMigrated)
+ animateInIconTranslation()
if (isVisible.value) {
CrossFadeHelper.fadeIn(this, animatorListener)
} else {
@@ -419,7 +411,7 @@
}
!isVisible.value -> {
// Let's make sure the icon are translated to 0, since we cancelled it above
- animateInIconTranslation(statusViewMigrated)
+ animateInIconTranslation()
CrossFadeHelper.fadeOut(this, animatorListener)
}
visibility != View.VISIBLE -> {
@@ -429,13 +421,12 @@
appearIcons(
animate = screenOffAnimationController.shouldAnimateAodIcons(),
iconsAppearTranslationPx,
- statusViewMigrated,
animatorListener,
)
}
else -> {
// Let's make sure the icons are translated to 0, since we cancelled it above
- animateInIconTranslation(statusViewMigrated)
+ animateInIconTranslation()
// We were fading out, let's fade in instead
CrossFadeHelper.fadeIn(this, animatorListener)
}
@@ -445,11 +436,10 @@
private fun View.appearIcons(
animate: Boolean,
iconAppearTranslation: Int,
- statusViewMigrated: Boolean,
animatorListener: Animator.AnimatorListener,
) {
if (animate) {
- if (!statusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
translationY = -iconAppearTranslation.toFloat()
}
alpha = 0f
@@ -457,19 +447,19 @@
.alpha(1f)
.setInterpolator(Interpolators.LINEAR)
.setDuration(AOD_ICONS_APPEAR_DURATION)
- .apply { if (statusViewMigrated) animateInIconTranslation() }
+ .apply { if (KeyguardShadeMigrationNssl.isEnabled) animateInIconTranslation() }
.setListener(animatorListener)
.start()
} else {
alpha = 1.0f
- if (!statusViewMigrated) {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
translationY = 0f
}
}
}
- private fun View.animateInIconTranslation(statusViewMigrated: Boolean) {
- if (!statusViewMigrated) {
+ private fun View.animateInIconTranslation() {
+ if (!KeyguardShadeMigrationNssl.isEnabled) {
animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
index c54203c..c6dfcb0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsButtonOnTouchListener.kt
@@ -20,12 +20,10 @@
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
-import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.view.rawDistanceFrom
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
class KeyguardSettingsButtonOnTouchListener(
- private val view: LaunchableLinearLayout,
private val viewModel: KeyguardSettingsMenuViewModel,
) : View.OnTouchListener {
@@ -41,8 +39,10 @@
MotionEvent.ACTION_UP -> {
view.isPressed = false
val distanceMoved =
- motionEvent
- .rawDistanceFrom(downPositionDisplayCoords.x, downPositionDisplayCoords.y)
+ motionEvent.rawDistanceFrom(
+ downPositionDisplayCoords.x,
+ downPositionDisplayCoords.y
+ )
val isClick = distanceMoved < ViewConfiguration.getTouchSlop()
viewModel.onTouchGestureEnded(isClick)
if (isClick) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 11e63e7..f67cb68 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -23,7 +23,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
@@ -43,15 +42,13 @@
object KeyguardSettingsViewBinder {
fun bind(
- parentView: View,
+ view: View,
viewModel: KeyguardSettingsMenuViewModel,
longPressViewModel: KeyguardLongPressViewModel,
- rootViewModel: KeyguardRootViewModel,
+ rootViewModel: KeyguardRootViewModel?,
vibratorHelper: VibratorHelper,
activityStarter: ActivityStarter
): DisposableHandle {
- val view = parentView.requireViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button)
-
val disposableHandle =
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -62,7 +59,6 @@
vibratorHelper.vibrate(KeyguardBottomAreaVibrations.Activated)
view.setOnTouchListener(
KeyguardSettingsButtonOnTouchListener(
- view = view,
viewModel = viewModel,
)
)
@@ -96,7 +92,7 @@
}
launch {
- rootViewModel.lastRootViewTapPosition.filterNotNull().collect { point ->
+ rootViewModel?.lastRootViewTapPosition?.filterNotNull()?.collect { point ->
if (view.isVisible) {
val hitRect = Rect()
view.getHitRect(hitRect)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 954d2cf..92270ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -16,13 +16,20 @@
package com.android.systemui.keyguard.ui.binder
+import android.transition.TransitionManager
+import android.view.View
+import androidx.constraintlayout.helper.widget.Layer
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
+import kotlinx.coroutines.launch
object KeyguardSmartspaceViewBinder {
@JvmStatic
@@ -30,15 +37,66 @@
smartspaceSection: SmartspaceSection,
keyguardRootView: ConstraintLayout,
clockViewModel: KeyguardClockViewModel,
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) {
keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- clockViewModel.hasCustomWeatherDataDisplay.collect {
- val constraintSet = ConstraintSet().apply { clone(keyguardRootView) }
- smartspaceSection.applyConstraints(constraintSet)
- constraintSet.applyTo(keyguardRootView)
+ launch {
+ clockViewModel.hasCustomWeatherDataDisplay.collect { hasCustomWeatherDataDisplay
+ ->
+ if (hasCustomWeatherDataDisplay) {
+ removeDateWeatherToBurnInLayer(keyguardRootView, smartspaceViewModel)
+ } else {
+ addDateWeatherToBurnInLayer(keyguardRootView, smartspaceViewModel)
+ }
+ clockViewModel.burnInLayer?.updatePostLayout(keyguardRootView)
+ val constraintSet = ConstraintSet().apply { clone(keyguardRootView) }
+ smartspaceSection.applyConstraints(constraintSet)
+ TransitionManager.beginDelayedTransition(keyguardRootView)
+ constraintSet.applyTo(keyguardRootView)
+ }
}
}
}
}
+
+ private fun addDateWeatherToBurnInLayer(
+ constraintLayout: ConstraintLayout,
+ smartspaceViewModel: KeyguardSmartspaceViewModel
+ ) {
+ val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
+ burnInLayer.apply {
+ if (
+ smartspaceViewModel.isSmartspaceEnabled &&
+ smartspaceViewModel.isDateWeatherDecoupled
+ ) {
+ val dateView =
+ constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
+ val weatherView =
+ constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
+ addView(weatherView)
+ addView(dateView)
+ }
+ }
+ }
+
+ private fun removeDateWeatherToBurnInLayer(
+ constraintLayout: ConstraintLayout,
+ smartspaceViewModel: KeyguardSmartspaceViewModel
+ ) {
+ val burnInLayer = constraintLayout.requireViewById<Layer>(R.id.burn_in_layer)
+ burnInLayer.apply {
+ if (
+ smartspaceViewModel.isSmartspaceEnabled &&
+ smartspaceViewModel.isDateWeatherDecoupled
+ ) {
+ val dateView =
+ constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
+ val weatherView =
+ constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
+ removeView(weatherView)
+ removeView(dateView)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
deleted file mode 100644
index 52d87d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
+++ /dev/null
@@ -1,61 +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.keyguard.ui.binder
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
-
-@ExperimentalCoroutinesApi
-object UdfpsAodFingerprintViewBinder {
-
- /**
- * Drives UI for the UDFPS aod fingerprint view. See [UdfpsFingerprintViewBinder] and
- * [UdfpsBackgroundViewBinder].
- */
- @JvmStatic
- fun bind(
- view: LottieAnimationView,
- viewModel: UdfpsAodViewModel,
- ) {
- view.alpha = 0f
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.burnInOffsets.collect { burnInOffsets ->
- view.progress = burnInOffsets.progress
- view.translationX = burnInOffsets.x.toFloat()
- view.translationY = burnInOffsets.y.toFloat()
- }
- }
-
- launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
-
- launch {
- viewModel.padding.collect { padding ->
- view.setPadding(padding, padding, padding, padding)
- }
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
deleted file mode 100644
index 0113628..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
+++ /dev/null
@@ -1,55 +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.keyguard.ui.binder
-
-import android.content.res.ColorStateList
-import android.widget.ImageView
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
-
-@ExperimentalCoroutinesApi
-object UdfpsBackgroundViewBinder {
-
- /**
- * Drives UI for the udfps background view. See [UdfpsAodFingerprintViewBinder] and
- * [UdfpsFingerprintViewBinder].
- */
- @JvmStatic
- fun bind(
- view: ImageView,
- viewModel: BackgroundViewModel,
- ) {
- view.alpha = 0f
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.transition.collect {
- view.alpha = it.alpha
- view.scaleX = it.scale
- view.scaleY = it.scale
- view.imageTintList = ColorStateList.valueOf(it.color)
- }
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
deleted file mode 100644
index d4621e6..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
+++ /dev/null
@@ -1,87 +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.keyguard.ui.binder
-
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.airbnb.lottie.LottieAnimationView
-import com.airbnb.lottie.LottieProperty
-import com.airbnb.lottie.model.KeyPath
-import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
-
-@ExperimentalCoroutinesApi
-object UdfpsFingerprintViewBinder {
- private var udfpsIconColor = 0
-
- /**
- * Drives UI for the UDFPS fingerprint view when it's NOT on aod. See
- * [UdfpsAodFingerprintViewBinder] and [UdfpsBackgroundViewBinder].
- */
- @JvmStatic
- fun bind(
- view: LottieAnimationView,
- viewModel: FingerprintViewModel,
- ) {
- view.alpha = 0f
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.transition.collect {
- view.alpha = it.alpha
- view.scaleX = it.scale
- view.scaleY = it.scale
- if (udfpsIconColor != (it.color)) {
- udfpsIconColor = it.color
- view.invalidate()
- }
- }
- }
-
- launch {
- viewModel.burnInOffsets.collect { burnInOffsets ->
- view.translationX = burnInOffsets.x.toFloat()
- view.translationY = burnInOffsets.y.toFloat()
- }
- }
-
- launch {
- viewModel.dozeAmount.collect { dozeAmount ->
- // Lottie progress represents: aod=0 to lockscreen=1
- view.progress = 1f - dozeAmount
- }
- }
-
- launch {
- viewModel.padding.collect { padding ->
- view.setPadding(padding, padding, padding, padding)
- }
- }
- }
- }
-
- // Add a callback that updates the color to `udfpsIconColor` whenever invalidate is called
- view.addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER) {
- PorterDuffColorFilter(udfpsIconColor, PorterDuff.Mode.SRC_ATOP)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
deleted file mode 100644
index aabb3f4..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
+++ /dev/null
@@ -1,55 +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.biometrics.ui.binder
-
-import android.view.View
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.ui.binder.UdfpsAodFingerprintViewBinder
-import com.android.systemui.keyguard.ui.binder.UdfpsBackgroundViewBinder
-import com.android.systemui.keyguard.ui.binder.UdfpsFingerprintViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
-import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-@ExperimentalCoroutinesApi
-object UdfpsKeyguardInternalViewBinder {
-
- @JvmStatic
- fun bind(
- view: View,
- viewModel: UdfpsKeyguardInternalViewModel,
- aodViewModel: UdfpsAodViewModel,
- fingerprintViewModel: FingerprintViewModel,
- backgroundViewModel: BackgroundViewModel,
- ) {
- view.accessibilityDelegate = viewModel.accessibilityDelegate
-
- // bind child views
- UdfpsAodFingerprintViewBinder.bind(view.requireViewById(R.id.udfps_aod_fp), aodViewModel)
- UdfpsFingerprintViewBinder.bind(
- view.requireViewById(R.id.udfps_lockscreen_fp),
- fingerprintViewModel
- )
- UdfpsBackgroundViewBinder.bind(
- view.requireViewById(R.id.udfps_keyguard_fp_bg),
- backgroundViewModel
- )
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
deleted file mode 100644
index 475d26f..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
+++ /dev/null
@@ -1,111 +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.keyguard.ui.binder
-
-import android.graphics.RectF
-import android.view.View
-import android.widget.FrameLayout
-import androidx.asynclayoutinflater.view.AsyncLayoutInflater
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.biometrics.UdfpsKeyguardView
-import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
-import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
-import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.launch
-
-@ExperimentalCoroutinesApi
-object UdfpsKeyguardViewBinder {
- /**
- * Drives UI for the keyguard UDFPS view. Inflates child views on a background thread. For view
- * binders for its child views, see [UdfpsFingerprintViewBinder], [UdfpsBackgroundViewBinder] &
- * [UdfpsAodFingerprintViewBinder].
- */
- @JvmStatic
- fun bind(
- view: UdfpsKeyguardView,
- viewModel: UdfpsKeyguardViewModel,
- udfpsKeyguardInternalViewModel: UdfpsKeyguardInternalViewModel,
- aodViewModel: UdfpsAodViewModel,
- fingerprintViewModel: FingerprintViewModel,
- backgroundViewModel: BackgroundViewModel,
- ) {
- val layoutInflaterFinishListener =
- AsyncLayoutInflater.OnInflateFinishedListener { inflatedInternalView, _, parent ->
- UdfpsKeyguardInternalViewBinder.bind(
- inflatedInternalView,
- udfpsKeyguardInternalViewModel,
- aodViewModel,
- fingerprintViewModel,
- backgroundViewModel,
- )
- val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
- lp.width = viewModel.sensorBounds.width()
- lp.height = viewModel.sensorBounds.height()
- val relativeToView =
- getBoundsRelativeToView(
- inflatedInternalView,
- RectF(viewModel.sensorBounds),
- )
- lp.setMarginsRelative(
- relativeToView.left.toInt(),
- relativeToView.top.toInt(),
- relativeToView.right.toInt(),
- relativeToView.bottom.toInt(),
- )
- parent!!.addView(inflatedInternalView, lp)
- }
- val inflater = AsyncLayoutInflater(view.context)
- inflater.inflate(R.layout.udfps_keyguard_view_internal, view, layoutInflaterFinishListener)
-
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- combine(aodViewModel.isVisible, fingerprintViewModel.visible) {
- isAodVisible,
- isFingerprintVisible ->
- isAodVisible || isFingerprintVisible
- }
- .collect { view.setVisible(it) }
- }
- }
- }
- }
-
- /**
- * Converts coordinates of RectF relative to the screen to coordinates relative to this view.
- *
- * @param bounds RectF based off screen coordinates in current orientation
- */
- private fun getBoundsRelativeToView(view: View, bounds: RectF): RectF {
- val pos: IntArray = view.locationOnScreen
- return RectF(
- bounds.left - pos[0],
- bounds.top - pos[1],
- bounds.right - pos[0],
- bounds.bottom - pos[1]
- )
- }
-}
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 24240df..940d1e1 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
@@ -142,6 +142,11 @@
return true
}
+ if (renderer == null || onDestroy == null) {
+ Log.wtf(TAG, "Renderer/onDestroy should not be null.")
+ return true
+ }
+
when (message.what) {
KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 1c6a2ab..bc9671e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -31,18 +31,21 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule.Companion.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
import kotlin.jvm.optionals.getOrNull
+import kotlinx.coroutines.ExperimentalCoroutinesApi
/**
* Positions elements of the lockscreen to the default position.
*
* This will be the most common use case for phones in portrait mode.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
@JvmSuppressWildcards
class DefaultKeyguardBlueprint
@@ -62,6 +65,7 @@
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
clockSection: ClockSection,
smartspaceSection: SmartspaceSection,
+ udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection,
) : KeyguardBlueprint {
override val id: String = DEFAULT
@@ -79,7 +83,8 @@
aodBurnInSection,
communalTutorialIndicatorSection,
clockSection,
- defaultDeviceEntrySection, // Add LAST: Intentionally has z-order above other views.
+ defaultDeviceEntrySection,
+ udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index bf70682..9b40433 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -29,14 +29,17 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
+import kotlinx.coroutines.ExperimentalCoroutinesApi
/** Vertically aligns the shortcuts with the udfps. */
+@ExperimentalCoroutinesApi
@SysUISingleton
class ShortcutsBesideUdfpsKeyguardBlueprint
@Inject
@@ -53,6 +56,7 @@
defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
+ udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection,
) : KeyguardBlueprint {
override val id: String = SHORTCUTS_BESIDE_UDFPS
@@ -68,7 +72,8 @@
splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
- defaultDeviceEntrySection, // Add LAST: Intentionally has z-order above other views.
+ defaultDeviceEntrySection,
+ udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index f890ae6..5344696b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -30,7 +30,10 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeClockSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeMediaSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
@@ -59,6 +62,9 @@
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
+ smartspaceSection: SmartspaceSection,
+ clockSection: SplitShadeClockSection,
+ mediaSection: SplitShadeMediaSection,
) : KeyguardBlueprint {
override val id: String = ID
@@ -73,8 +79,11 @@
splitShadeNotificationStackScrollLayoutSection,
splitShadeGuidelines,
aodNotificationIconsSection,
+ smartspaceSection,
aodBurnInSection,
communalTutorialIndicatorSection,
+ clockSection,
+ mediaSection,
defaultDeviceEntrySection, // Add LAST: Intentionally has z-order above other views.
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index 8166b45..1ccc6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -23,12 +23,12 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.Flags.migrateClocksToBlueprint
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
import javax.inject.Inject
/** Adds a layer to group elements for translation for burn-in preventation */
@@ -38,7 +38,6 @@
private val context: Context,
private val clockViewModel: KeyguardClockViewModel,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
- private val featureFlags: FeatureFlagsClassic,
) : KeyguardSection() {
lateinit var burnInLayer: Layer
@@ -59,6 +58,8 @@
}
}
if (migrateClocksToBlueprint()) {
+ // weather and date parts won't be added here, cause their visibility doesn't align
+ // with others in burnInLayer
addSmartspaceViews(constraintLayout)
}
constraintLayout.addView(burnInLayer)
@@ -87,16 +88,8 @@
burnInLayer.apply {
if (smartspaceViewModel.isSmartspaceEnabled) {
val smartspaceView =
- constraintLayout.requireViewById<View>(smartspaceViewModel.smartspaceViewId)
+ constraintLayout.requireViewById<View>(sharedR.id.bc_smartspace_view)
addView(smartspaceView)
- if (smartspaceViewModel.isDateWeatherDecoupled) {
- val dateView =
- constraintLayout.requireViewById<View>(smartspaceViewModel.dateId)
- val weatherView =
- constraintLayout.requireViewById<View>(smartspaceViewModel.weatherId)
- addView(weatherView)
- addView(dateView)
- }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 39a0547..f560b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -28,11 +28,11 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
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
@@ -49,7 +49,6 @@
constructor(
private val context: Context,
private val configurationState: ConfigurationState,
- private val featureFlags: FeatureFlagsClassic,
private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
@@ -119,14 +118,8 @@
}
constraintSet.apply {
if (migrateClocksToBlueprint()) {
- connect(
- nicId,
- TOP,
- smartspaceViewModel.smartspaceViewId,
- topAlignment,
- bottomMargin
- )
- setGoneMargin(nicId, topAlignment, bottomMargin)
+ connect(nicId, TOP, sharedR.id.bc_smartspace_view, BOTTOM, bottomMargin)
+ setGoneMargin(nicId, BOTTOM, bottomMargin)
} else {
connect(nicId, TOP, R.id.keyguard_status_view, topAlignment, bottomMargin)
}
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 1df920a..b5f32c8 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
@@ -27,12 +27,11 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardClockViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceLayout
import com.android.systemui.res.R
@@ -50,19 +49,20 @@
alpha: Float,
) = views.forEach { view -> this.setAlpha(view.id, alpha) }
-class ClockSection
+open class ClockSection
@Inject
constructor(
private val clockInteractor: KeyguardClockInteractor,
- private val keyguardClockViewModel: KeyguardClockViewModel,
- val smartspaceViewModel: KeyguardSmartspaceViewModel,
+ protected val keyguardClockViewModel: KeyguardClockViewModel,
private val context: Context,
private val splitShadeStateController: SplitShadeStateController,
- private val featureFlags: FeatureFlagsClassic,
) : KeyguardSection() {
override fun addViews(constraintLayout: ConstraintLayout) {}
override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!Flags.migrateClocksToBlueprint()) {
+ return
+ }
KeyguardClockViewBinder.bind(
this,
constraintLayout,
@@ -72,6 +72,9 @@
}
override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!Flags.migrateClocksToBlueprint()) {
+ return
+ }
clockInteractor.clock?.let { clock ->
constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
}
@@ -94,16 +97,6 @@
}
}
- var largeClockEndGuideline = PARENT_ID
-
- // Return if largeClockEndGuideline changes,
- // and use it to decide whether to refresh blueprint
- fun setClockShouldBeCentered(shouldBeCentered: Boolean): Boolean {
- val previousValue = largeClockEndGuideline
- largeClockEndGuideline = if (shouldBeCentered) PARENT_ID else R.id.split_shade_guideline
- return previousValue != largeClockEndGuideline
- }
-
private fun getTargetClockFace(clock: ClockController): ClockFaceLayout =
if (keyguardClockViewModel.useLargeClock) getLargeClockFace(clock)
else getSmallClockFace(clock)
@@ -113,10 +106,10 @@
private fun getLargeClockFace(clock: ClockController): ClockFaceLayout = clock.largeClock.layout
private fun getSmallClockFace(clock: ClockController): ClockFaceLayout = clock.smallClock.layout
- fun applyDefaultConstraints(constraints: ConstraintSet) {
+ open fun applyDefaultConstraints(constraints: ConstraintSet) {
constraints.apply {
connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START)
- connect(R.id.lockscreen_clock_view_large, END, largeClockEndGuideline, END)
+ connect(R.id.lockscreen_clock_view_large, END, PARENT_ID, END)
connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP)
var largeClockTopMargin =
context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
@@ -124,8 +117,8 @@
com.android.systemui.customization.R.dimen.small_clock_padding_top
) +
context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
- largeClockTopMargin += smartspaceViewModel.getDimen(DATE_WEATHER_VIEW_HEIGHT)
- largeClockTopMargin += smartspaceViewModel.getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+ largeClockTopMargin += getDimen(DATE_WEATHER_VIEW_HEIGHT)
+ largeClockTopMargin += getDimen(ENHANCED_SMARTSPACE_HEIGHT)
if (!keyguardClockViewModel.useLargeClock) {
largeClockTopMargin -=
context.resources.getDimensionPixelSize(
@@ -168,6 +161,12 @@
}
}
+ private fun getDimen(name: String): Int {
+ val res = context.packageManager.getResourcesForApplication(context.packageName)
+ val id = res.getIdentifier(name, "dimen", context.packageName)
+ return res.getDimensionPixelSize(id)
+ }
+
companion object {
private const val DATE_WEATHER_VIEW_HEIGHT = "date_weather_view_height"
private const val ENHANCED_SMARTSPACE_HEIGHT = "enhanced_smartspace_height"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index 56f717d..66c137f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -54,7 +54,7 @@
if (keyguardBottomAreaRefactor()) {
indicationAreaHandle =
KeyguardIndicationAreaBinder.bind(
- constraintLayout,
+ constraintLayout.requireViewById(R.id.keyguard_indication_area),
keyguardIndicationAreaViewModel,
keyguardRootViewModel,
indicationController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index e7b6e44..b0eee0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -31,6 +31,7 @@
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
@@ -80,7 +81,7 @@
connect(
R.id.nssl_placeholder,
TOP,
- smartspaceViewModel.smartspaceViewId,
+ sharedR.id.bc_smartspace_view,
BOTTOM,
bottomMargin
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
new file mode 100644
index 0000000..3265d79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultUdfpsAccessibilityOverlaySection.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.Flags
+import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
+import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
+import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Positions the UDFPS accessibility overlay on the bottom half of the keyguard. */
+@ExperimentalCoroutinesApi
+class DefaultUdfpsAccessibilityOverlaySection
+@Inject
+constructor(
+ private val context: Context,
+ private val viewModel: DeviceEntryUdfpsAccessibilityOverlayViewModel,
+) : KeyguardSection() {
+ private val viewId = R.id.udfps_accessibility_overlay
+ private var cachedConstraintLayout: ConstraintLayout? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ cachedConstraintLayout = constraintLayout
+ constraintLayout.addView(UdfpsAccessibilityOverlay(context).apply { id = viewId })
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ UdfpsAccessibilityOverlayBinder.bind(
+ constraintLayout.findViewById(viewId)!!,
+ viewModel,
+ )
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ constraintSet.apply {
+ connect(viewId, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START)
+ connect(viewId, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END)
+
+ create(R.id.udfps_accessibility_overlay_top_guideline, ConstraintSet.HORIZONTAL)
+ setGuidelinePercent(R.id.udfps_accessibility_overlay_top_guideline, .5f)
+ connect(
+ viewId,
+ ConstraintSet.TOP,
+ R.id.udfps_accessibility_overlay_top_guideline,
+ ConstraintSet.BOTTOM,
+ )
+
+ if (Flags.keyguardBottomAreaRefactor()) {
+ connect(
+ viewId,
+ ConstraintSet.BOTTOM,
+ R.id.keyguard_indication_area,
+ ConstraintSet.TOP,
+ )
+ } else {
+ connect(viewId, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
+ }
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.removeView(viewId)
+ }
+}
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 368b388..eacd466 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
@@ -36,7 +36,7 @@
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
-class SmartspaceSection
+open class SmartspaceSection
@Inject
constructor(
val keyguardClockViewModel: KeyguardClockViewModel,
@@ -67,10 +67,21 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- KeyguardSmartspaceViewBinder.bind(this, constraintLayout, keyguardClockViewModel)
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
+ KeyguardSmartspaceViewBinder.bind(
+ this,
+ constraintLayout,
+ keyguardClockViewModel,
+ keyguardSmartspaceViewModel,
+ )
}
override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
// Generally, weather should be next to dateView
// smartspace should be below date & weather views
constraintSet.apply {
@@ -130,7 +141,20 @@
}
}
}
- updateVisibility(constraintSet)
+ }
+ updateVisibility(constraintSet)
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
+ listOf(smartspaceView, dateView, weatherView).forEach {
+ it?.let {
+ if (it.parent == constraintLayout) {
+ constraintLayout.removeView(it)
+ }
+ }
}
}
@@ -158,14 +182,4 @@
}
}
}
-
- override fun removeViews(constraintLayout: ConstraintLayout) {
- listOf(smartspaceView, dateView, weatherView).forEach {
- it?.let {
- if (it.parent == constraintLayout) {
- constraintLayout.removeView(it)
- }
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt
new file mode 100644
index 0000000..19ba1aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeClockSection.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.SplitShadeStateController
+import javax.inject.Inject
+
+class SplitShadeClockSection
+@Inject
+constructor(
+ clockInteractor: KeyguardClockInteractor,
+ keyguardClockViewModel: KeyguardClockViewModel,
+ context: Context,
+ splitShadeStateController: SplitShadeStateController,
+) : ClockSection(clockInteractor, keyguardClockViewModel, context, splitShadeStateController) {
+ override fun applyDefaultConstraints(constraints: ConstraintSet) {
+ super.applyDefaultConstraints(constraints)
+ val largeClockEndGuideline =
+ if (keyguardClockViewModel.clockShouldBeCentered.value) ConstraintSet.PARENT_ID
+ else R.id.split_shade_guideline
+ constraints.apply {
+ connect(
+ R.id.lockscreen_clock_view_large,
+ ConstraintSet.END,
+ largeClockEndGuideline,
+ ConstraintSet.END
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
new file mode 100644
index 0000000..f20ab06
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+import androidx.constraintlayout.widget.Barrier
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.Flags.migrateClocksToBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shared.R as sharedR
+import javax.inject.Inject
+
+/** Aligns media on left side for split shade, below smartspace, date, and weather. */
+class SplitShadeMediaSection
+@Inject
+constructor(
+ private val context: Context,
+ private val notificationPanelView: NotificationPanelView,
+ private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
+ private val keyguardMediaController: KeyguardMediaController
+) : KeyguardSection() {
+ private val mediaContainerId = R.id.status_view_media_container
+ private val smartSpaceBarrier = R.id.smart_space_barrier_bottom
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
+
+ notificationPanelView.findViewById<View>(mediaContainerId)?.let {
+ notificationPanelView.removeView(it)
+ }
+
+ val mediaFrame =
+ FrameLayout(context, null).apply {
+ id = mediaContainerId
+ val padding = context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
+ val horizontalPadding =
+ padding +
+ context.resources.getDimensionPixelSize(
+ R.dimen.status_view_margin_horizontal
+ )
+
+ setPaddingRelative(horizontalPadding, padding, horizontalPadding, padding)
+ }
+ constraintLayout.addView(mediaFrame)
+ keyguardMediaController.attachSplitShadeContainer(mediaFrame)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {}
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
+
+ constraintSet.apply {
+ constrainWidth(mediaContainerId, MATCH_CONSTRAINT)
+ constrainHeight(mediaContainerId, WRAP_CONTENT)
+
+ createBarrier(
+ smartSpaceBarrier,
+ Barrier.BOTTOM,
+ 0,
+ *intArrayOf(
+ sharedR.id.bc_smartspace_view,
+ sharedR.id.date_smartspace_view,
+ sharedR.id.weather_smartspace_view,
+ )
+ )
+ connect(mediaContainerId, TOP, smartSpaceBarrier, BOTTOM)
+ connect(mediaContainerId, START, PARENT_ID, START)
+ connect(mediaContainerId, END, R.id.split_shade_guideline, END)
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ if (!migrateClocksToBlueprint()) {
+ return
+ }
+
+ constraintLayout.removeView(mediaContainerId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index b0b5c81..0f8e673 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -23,7 +23,6 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
@@ -72,25 +71,9 @@
return
}
constraintSet.apply {
- val bottomMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
-
- if (migrateClocksToBlueprint()) {
- connect(
- R.id.nssl_placeholder,
- TOP,
- smartspaceViewModel.smartspaceViewId,
- TOP,
- bottomMargin
- )
- setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin)
- } else {
- val splitShadeTopMargin =
- context.resources.getDimensionPixelSize(
- R.dimen.large_screen_shade_header_height
- )
- connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin)
- }
+ val splitShadeTopMargin =
+ context.resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+ connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin)
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
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
new file mode 100644
index 0000000..6846886
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Provides dependencies for the AlternateBouncerViewBinder. */
+@ExperimentalCoroutinesApi
+class AlternateBouncerDependencies
+@Inject
+constructor(
+ val viewModel: AlternateBouncerViewModel,
+ val falsingManager: FalsingManager,
+ val swipeUpAnywhereGestureHandler: SwipeUpAnywhereGestureHandler,
+ val tapGestureDetector: TapGestureDetector,
+ val udfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
+ val udfpsAccessibilityOverlayViewModel:
+ Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index e18893a..fa4de04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -20,12 +20,12 @@
import android.hardware.biometrics.SensorLocationInternal
import com.android.settingslib.Utils
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.res.R
import javax.inject.Inject
-import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -43,7 +43,9 @@
val context: Context,
configurationInteractor: ConfigurationInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel,
fingerprintPropertyRepository: FingerprintPropertyRepository,
+ udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
val iconLocation: Flow<IconLocation> =
@@ -72,11 +74,7 @@
.onStart {
emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
}
- private val fgIconPadding: Flow<Int> =
- configurationInteractor.scaleForResolution.map { scale ->
- (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
- .roundToInt()
- }
+ private val fgIconPadding: Flow<Int> = udfpsOverlayInteractor.iconPadding
val fgViewModel: Flow<DeviceEntryForegroundViewModel.ForegroundIconViewModel> =
combine(
fgIconColor,
@@ -90,26 +88,8 @@
)
}
- private val bgColor: Flow<Int> =
- configurationInteractor.onAnyConfigurationChange
- .map {
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorSurface)
- }
- .onStart {
- emit(
- Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.colorSurface
- )
- )
- }
- val bgViewModel: Flow<DeviceEntryBackgroundViewModel.BackgroundViewModel> =
- bgColor.map { color ->
- DeviceEntryBackgroundViewModel.BackgroundViewModel(
- alpha = 1f,
- tint = color,
- )
- }
+ val bgColor: Flow<Int> = deviceEntryBackgroundViewModel.color
+ val bgAlpha: Flow<Float> = flowOf(1f)
data class IconLocation(
val left: Int,
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 c45caf0..be9ae1d 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
@@ -19,11 +19,10 @@
import android.content.Context
import com.android.settingslib.Utils
-import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
@@ -34,7 +33,7 @@
@Inject
constructor(
val context: Context,
- configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor
+ configurationInteractor: ConfigurationInteractor,
lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
@@ -44,8 +43,8 @@
dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
) {
- private val color: Flow<Int> =
- configurationRepository.onAnyConfigurationChange
+ val color: Flow<Int> =
+ configurationInteractor.onAnyConfigurationChange
.map {
Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorSurface)
}
@@ -57,7 +56,7 @@
)
)
}
- private val alpha: Flow<Float> =
+ val alpha: Flow<Float> =
setOf(
lockscreenToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
aodToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
@@ -69,17 +68,4 @@
alternateBouncerToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
)
.merge()
-
- val viewModel: Flow<BackgroundViewModel> =
- combine(color, alpha) { color, alpha ->
- BackgroundViewModel(
- alpha = alpha,
- tint = color,
- )
- }
-
- data class BackgroundViewModel(
- val alpha: Float,
- val tint: Int,
- )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 3aeff61..528a2ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -27,8 +27,8 @@
import com.android.systemui.plugins.clocks.ClockController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
@@ -79,10 +79,10 @@
?: false
)
- val clockShouldBeCentered: Flow<Boolean> =
+ val clockShouldBeCentered: StateFlow<Boolean> =
keyguardInteractor.clockShouldBeCentered.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = true
+ initialValue = false
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index 4541458..a1dd720 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -16,42 +16,66 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.content.Context
-import android.util.Log
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
@SysUISingleton
class KeyguardSmartspaceViewModel
@Inject
-constructor(val context: Context, smartspaceController: LockscreenSmartspaceController) {
+constructor(
+ @Application applicationScope: CoroutineScope,
+ smartspaceController: LockscreenSmartspaceController,
+ keyguardClockViewModel: KeyguardClockViewModel,
+) {
+ /** Whether the smartspace section is available in the build. */
val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled()
+ /** Whether the weather area is available in the build. */
+ // TODO(b/317891876): this should be a Flow as the value can change over time.
val isWeatherEnabled: Boolean = smartspaceController.isWeatherEnabled()
+ /** Whether the data and weather areas are decoupled in the build. */
val isDateWeatherDecoupled: Boolean = smartspaceController.isDateWeatherDecoupled()
- val smartspaceViewId: Int
- get() = getId("bc_smartspace_view")
- val dateId: Int
- get() = getId("date_smartspace_view")
+ /** Whether the date area should be visible. */
+ val isDateVisible: StateFlow<Boolean> =
+ keyguardClockViewModel.hasCustomWeatherDataDisplay
+ .map { !it }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
+ )
- val weatherId: Int
- get() = getId("weather_smartspace_view")
-
- private fun getId(name: String): Int {
- return context.resources.getIdentifier(name, "id", context.packageName).also {
- if (it == 0) {
- Log.d(TAG, "Cannot resolve id $name")
+ /** Whether the weather area should be visible. */
+ val isWeatherVisible: StateFlow<Boolean> =
+ keyguardClockViewModel.hasCustomWeatherDataDisplay
+ .map { clockIncludesCustomWeatherDisplay ->
+ isWeatherVisible(
+ clockIncludesCustomWeatherDisplay = clockIncludesCustomWeatherDisplay,
+ isWeatherEnabled = isWeatherEnabled,
+ )
}
- }
- }
- fun getDimen(name: String): Int {
- val res = context.packageManager.getResourcesForApplication(context.packageName)
- val id = res.getIdentifier(name, "dimen", context.packageName)
- return res.getDimensionPixelSize(id)
- }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue =
+ isWeatherVisible(
+ clockIncludesCustomWeatherDisplay =
+ keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
+ isWeatherEnabled = isWeatherEnabled,
+ )
+ )
- companion object {
- private val TAG = KeyguardSmartspaceViewModel::class.java.simpleName
+ private fun isWeatherVisible(
+ clockIncludesCustomWeatherDisplay: Boolean,
+ isWeatherEnabled: Boolean,
+ ): Boolean {
+ return !clockIncludesCustomWeatherDisplay && isWeatherEnabled
}
}
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
new file mode 100644
index 0000000..36bbe4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class LockscreenContentViewModel
+@Inject
+constructor(
+ private val interactor: KeyguardBlueprintInteractor,
+ private val authController: AuthController,
+ val longPress: KeyguardLongPressViewModel,
+) {
+ val isUdfpsVisible: Boolean
+ get() = authController.isUdfpsSupported
+
+ fun blueprintId(scope: CoroutineScope): StateFlow<String> {
+ return interactor.blueprint
+ .map { it.id }
+ .distinctUntilChanged()
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = interactor.getCurrentBlueprint().id,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
deleted file mode 100644
index 6e77e13e..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
+++ /dev/null
@@ -1,47 +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.keyguard.ui.viewmodel
-
-import android.content.Context
-import com.android.systemui.keyguard.domain.interactor.Offsets
-import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
-import com.android.systemui.res.R
-import javax.inject.Inject
-import kotlin.math.roundToInt
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
-
-/** View-model for UDFPS AOD view. */
-@ExperimentalCoroutinesApi
-class UdfpsAodViewModel
-@Inject
-constructor(
- val interactor: UdfpsKeyguardInteractor,
- val context: Context,
-) {
- val alpha: Flow<Float> = interactor.dozeAmount
- val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
- val isVisible: Flow<Boolean> = alpha.map { it != 0f }
-
- // Padding between the fingerprint icon and its bounding box in pixels.
- val padding: Flow<Int> =
- interactor.scaleForResolution.map { scale ->
- (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
- .roundToInt()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt
deleted file mode 100644
index d894a11..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt
+++ /dev/null
@@ -1,26 +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.keyguard.ui.viewmodel
-
-import com.android.systemui.biometrics.UdfpsKeyguardAccessibilityDelegate
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-@ExperimentalCoroutinesApi
-class UdfpsKeyguardInternalViewModel
-@Inject
-constructor(val accessibilityDelegate: UdfpsKeyguardAccessibilityDelegate)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
deleted file mode 100644
index dca151d..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
+++ /dev/null
@@ -1,26 +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.keyguard.ui.viewmodel
-
-import android.graphics.Rect
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-@ExperimentalCoroutinesApi
-class UdfpsKeyguardViewModel @Inject constructor() {
- var sensorBounds: Rect = Rect()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt
deleted file mode 100644
index 098b481..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.systemui.keyguard.ui.viewmodel
-
-import android.graphics.Rect
-import com.android.systemui.biometrics.UdfpsKeyguardView
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.binder.UdfpsKeyguardViewBinder
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-@ExperimentalCoroutinesApi
-@SysUISingleton
-class UdfpsKeyguardViewModels
-@Inject
-constructor(
- private val viewModel: UdfpsKeyguardViewModel,
- private val internalViewModel: UdfpsKeyguardInternalViewModel,
- private val aodViewModel: UdfpsAodViewModel,
- private val lockscreenFingerprintViewModel: FingerprintViewModel,
- private val lockscreenBackgroundViewModel: BackgroundViewModel,
-) {
-
- fun setSensorBounds(sensorBounds: Rect) {
- viewModel.sensorBounds = sensorBounds
- }
-
- fun bindViews(view: UdfpsKeyguardView) {
- UdfpsKeyguardViewBinder.bind(
- view,
- viewModel,
- internalViewModel,
- aodViewModel,
- lockscreenFingerprintViewModel,
- lockscreenBackgroundViewModel
- )
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
deleted file mode 100644
index 642904d..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ /dev/null
@@ -1,220 +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.keyguard.ui.viewmodel
-
-import android.content.Context
-import androidx.annotation.ColorInt
-import com.android.settingslib.Utils.getColorAttrDefaultColor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.Offsets
-import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.res.R
-import com.android.wm.shell.animation.Interpolators
-import javax.inject.Inject
-import kotlin.math.roundToInt
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
-
-/** View-model for UDFPS lockscreen views. */
-@ExperimentalCoroutinesApi
-open class UdfpsLockscreenViewModel(
- context: Context,
- lockscreenColorResId: Int,
- alternateBouncerColorResId: Int,
- transitionInteractor: KeyguardTransitionInteractor,
- udfpsKeyguardInteractor: UdfpsKeyguardInteractor,
- keyguardInteractor: KeyguardInteractor,
-) {
- private val toLockscreen: Flow<TransitionViewModel> =
- transitionInteractor.anyStateToLockscreenTransition.map {
- TransitionViewModel(
- alpha =
- if (it.from == KeyguardState.AOD) {
- it.value // animate
- } else {
- 1f
- },
- scale = 1f,
- color = getColorAttrDefaultColor(context, lockscreenColorResId),
- )
- }
-
- private val toAlternateBouncer: Flow<TransitionViewModel> =
- keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
- transitionInteractor.transitionStepsToState(KeyguardState.ALTERNATE_BOUNCER).map {
- TransitionViewModel(
- alpha = 1f,
- scale =
- if (visibleInKeyguardState(it.from, statusBarState)) {
- 1f
- } else {
- Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
- },
- color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
- )
- }
- }
-
- private val fadeOut: Flow<TransitionViewModel> =
- keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
- merge(
- transitionInteractor.anyStateToGoneTransition,
- transitionInteractor.anyStateToAodTransition,
- transitionInteractor.anyStateToOccludedTransition,
- transitionInteractor.anyStateToPrimaryBouncerTransition,
- transitionInteractor.anyStateToDreamingTransition,
- )
- .map {
- TransitionViewModel(
- alpha =
- if (visibleInKeyguardState(it.from, statusBarState)) {
- 1f - it.value
- } else {
- 0f
- },
- scale = 1f,
- color =
- if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
- getColorAttrDefaultColor(context, alternateBouncerColorResId)
- } else {
- getColorAttrDefaultColor(context, lockscreenColorResId)
- },
- )
- }
- }
-
- private fun visibleInKeyguardState(
- state: KeyguardState,
- statusBarState: StatusBarState
- ): Boolean {
- return when (state) {
- KeyguardState.OFF,
- KeyguardState.DOZING,
- KeyguardState.DREAMING,
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
- KeyguardState.AOD,
- KeyguardState.PRIMARY_BOUNCER,
- KeyguardState.GONE,
- KeyguardState.OCCLUDED -> false
- KeyguardState.LOCKSCREEN -> statusBarState == StatusBarState.KEYGUARD
- KeyguardState.ALTERNATE_BOUNCER -> true
- }
- }
-
- private val keyguardStateTransition =
- merge(
- toAlternateBouncer,
- toLockscreen,
- fadeOut,
- )
-
- private val dialogHideAffordancesAlphaMultiplier: Flow<Float> =
- udfpsKeyguardInteractor.dialogHideAffordancesRequest.map { hideAffordances ->
- if (hideAffordances) {
- 0f
- } else {
- 1f
- }
- }
-
- private val alphaMultiplier: Flow<Float> =
- combine(
- transitionInteractor.startedKeyguardState,
- dialogHideAffordancesAlphaMultiplier,
- udfpsKeyguardInteractor.shadeExpansion,
- udfpsKeyguardInteractor.qsProgress,
- ) { startedKeyguardState, dialogHideAffordancesAlphaMultiplier, shadeExpansion, qsProgress
- ->
- if (startedKeyguardState == KeyguardState.ALTERNATE_BOUNCER) {
- 1f
- } else {
- dialogHideAffordancesAlphaMultiplier * (1f - shadeExpansion) * (1f - qsProgress)
- }
- }
-
- val transition: Flow<TransitionViewModel> =
- combine(
- alphaMultiplier,
- keyguardStateTransition,
- ) { alphaMultiplier, keyguardStateTransition ->
- TransitionViewModel(
- alpha = keyguardStateTransition.alpha * alphaMultiplier,
- scale = keyguardStateTransition.scale,
- color = keyguardStateTransition.color,
- )
- }
- val visible: Flow<Boolean> = transition.map { it.alpha != 0f }
-}
-
-@ExperimentalCoroutinesApi
-class FingerprintViewModel
-@Inject
-constructor(
- val context: Context,
- transitionInteractor: KeyguardTransitionInteractor,
- interactor: UdfpsKeyguardInteractor,
- keyguardInteractor: KeyguardInteractor,
-) :
- UdfpsLockscreenViewModel(
- context,
- android.R.attr.textColorPrimary,
- com.android.internal.R.attr.materialColorOnPrimaryFixed,
- transitionInteractor,
- interactor,
- keyguardInteractor,
- ) {
- val dozeAmount: Flow<Float> = interactor.dozeAmount
- val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
-
- // Padding between the fingerprint icon and its bounding box in pixels.
- val padding: Flow<Int> =
- interactor.scaleForResolution.map { scale ->
- (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
- .roundToInt()
- }
-}
-
-@ExperimentalCoroutinesApi
-class BackgroundViewModel
-@Inject
-constructor(
- val context: Context,
- transitionInteractor: KeyguardTransitionInteractor,
- interactor: UdfpsKeyguardInteractor,
- keyguardInteractor: KeyguardInteractor,
-) :
- UdfpsLockscreenViewModel(
- context,
- com.android.internal.R.attr.colorSurface,
- com.android.internal.R.attr.materialColorPrimaryFixed,
- transitionInteractor,
- interactor,
- keyguardInteractor,
- )
-
-data class TransitionViewModel(
- val alpha: Float,
- val scale: Float,
- @ColorInt val color: Int,
-)
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 d8bb3e6..0d5ba64 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -26,6 +26,7 @@
import com.android.systemui.log.echo.LogcatEchoTrackerProd;
import com.android.systemui.log.table.TableLogBuffer;
import com.android.systemui.log.table.TableLogBufferFactory;
+import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.pipeline.shared.TileSpec;
@@ -417,6 +418,18 @@
}
/**
+ * Provides a {@link ClockMessageBuffers} which contains the keyguard clock message buffers.
+ */
+ @Provides
+ public static ClockMessageBuffers provideKeyguardClockMessageBuffers(
+ @KeyguardClockLog LogBuffer infraClockLog,
+ @KeyguardSmallClockLog LogBuffer smallClockLog,
+ @KeyguardLargeClockLog LogBuffer largeClockLog
+ ) {
+ return new ClockMessageBuffers(infraClockLog, smallClockLog, largeClockLog);
+ }
+
+ /**
* Provides a {@link LogBuffer} for use by {@link com.android.keyguard.KeyguardUpdateMonitor}.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
index 724241d..185a783 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.pipeline
import android.content.Context
+import android.content.pm.UserInfo
import android.os.SystemProperties
import android.util.Log
import com.android.internal.annotations.KeepForWeakReference
@@ -88,7 +89,11 @@
private val userTrackerCallback =
object : UserTracker.Callback {
override fun onUserChanged(newUser: Int, userContext: Context) {
- handleUserSwitched(newUser)
+ handleUserSwitched()
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ handleProfileChanged()
}
}
@@ -109,7 +114,10 @@
}
allEntries.put(key, data)
- if (!lockscreenUserManager.isCurrentProfile(data.userId)) {
+ if (
+ !lockscreenUserManager.isCurrentProfile(data.userId) ||
+ !lockscreenUserManager.isProfileAvailable(data.userId)
+ ) {
return
}
@@ -231,7 +239,20 @@
}
@VisibleForTesting
- internal fun handleUserSwitched(id: Int) {
+ internal fun handleProfileChanged() {
+ // TODO(b/317221348) re-add media removed when profile is available.
+ allEntries.forEach { (key, data) ->
+ if (!lockscreenUserManager.isProfileAvailable(data.userId)) {
+ // Only remove media when the profile is unavailable.
+ if (DEBUG) Log.d(TAG, "Removing $key after profile change")
+ userEntries.remove(key, data)
+ listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal fun handleUserSwitched() {
// If the user changes, remove all current MediaData objects and inform listeners
val listenersCopy = listeners
val keyCopy = userEntries.keys.toMutableList()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 945bf9a..e15e038 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -27,6 +27,7 @@
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -180,7 +181,11 @@
/** Called whenever the media hosts visibility changes */
private fun onMediaHostVisibilityChanged(visible: Boolean) {
refreshMediaPosition(reason = "onMediaHostVisibilityChanged")
+
if (visible) {
+ if (migrateClocksToBlueprint() && useSplitShade) {
+ return
+ }
mediaHost.hostView.layoutParams.apply {
height = ViewGroup.LayoutParams.WRAP_CONTENT
width = ViewGroup.LayoutParams.MATCH_PARENT
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index e827a1e..3e6d46c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -25,12 +25,12 @@
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
+import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.ViewPriority
@@ -162,7 +162,7 @@
logger: MediaTttSenderLogger,
instanceId: InstanceId,
): ChipbarInfo {
- val packageName = checkNotNull(routeInfo.clientPackageName)
+ val packageName = routeInfo.clientPackageName
val otherDeviceName =
if (routeInfo.name.isBlank()) {
context.getString(R.string.media_ttt_default_device_type)
@@ -171,7 +171,7 @@
}
val icon =
MediaTttUtils.getIconInfoFromPackageName(context, packageName, isReceiver = false) {
- logger.logPackageNotFound(packageName)
+ packageName?.let { logger.logPackageNotFound(it) }
}
val timeout =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
index 3c50127..2408af1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartable.kt
@@ -17,23 +17,22 @@
package com.android.systemui.mediaprojection.taskswitcher
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.pssTaskSwitcher
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.taskswitcher.ui.TaskSwitcherNotificationCoordinator
+import dagger.Lazy
import javax.inject.Inject
@SysUISingleton
class MediaProjectionTaskSwitcherCoreStartable
@Inject
constructor(
- private val notificationCoordinator: TaskSwitcherNotificationCoordinator,
- private val featureFlags: FeatureFlags,
+ private val notificationCoordinatorLazy: Lazy<TaskSwitcherNotificationCoordinator>,
) : CoreStartable {
override fun start() {
- if (featureFlags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)) {
- notificationCoordinator.start()
+ if (pssTaskSwitcher()) {
+ notificationCoordinatorLazy.get().start()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 0e7e69b..3aa9daa 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -37,7 +37,7 @@
import android.provider.Settings
import android.widget.Toast
import androidx.annotation.VisibleForTesting
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -263,11 +263,7 @@
* If the shortcut entry `android:enabled` is set to `true`, the shortcut will be visible in the
* Widget Picker to all users.
*/
- // TODO(b/316332684)
- @Suppress("UNREACHABLE_CODE")
fun setNoteTaskShortcutEnabled(value: Boolean, user: UserHandle) {
- return // shortcut should not be enabled until additional features are implemented.
-
if (!userManager.isUserUnlocked(user)) {
debugLog { "setNoteTaskShortcutEnabled call but user locked: user=$user" }
return
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 1534653..958ace35 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -48,12 +48,13 @@
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -62,7 +63,10 @@
import javax.inject.Inject;
@SysUISingleton
-public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
+public class PowerUI implements
+ CoreStartable,
+ ConfigurationController.ConfigurationListener,
+ CommandQueue.Callbacks {
static final String TAG = "PowerUI";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -223,7 +227,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
// Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 7184fa0..8dd0ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -16,14 +16,19 @@
package com.android.systemui.power.dagger;
+import com.android.systemui.CoreStartable;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
import com.android.systemui.power.data.repository.PowerRepositoryModule;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import dagger.Binds;
import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
/** Dagger Module for code in the power package. */
@@ -33,6 +38,17 @@
}
)
public interface PowerModule {
+ /** Starts PowerUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PowerUI.class)
+ CoreStartable bindPowerUIStartable(PowerUI impl);
+
+ /** Listen to config changes for PowerUI. */
+ @Binds
+ @IntoSet
+ ConfigurationController.ConfigurationListener bindPowerUIConfigChanges(PowerUI impl);
+
/** */
@Binds
EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 1ab64b7..ba3357c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -17,12 +17,10 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
-import android.os.Build;
import android.provider.Settings;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.res.R;
import java.util.ArrayList;
import java.util.Arrays;
@@ -44,10 +42,6 @@
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
tiles.addAll(Arrays.asList(defaultTileList.split(",")));
- if (Build.IS_DEBUGGABLE
- && GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
- tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
- }
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 2af7ae0..47b0624 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -23,7 +23,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
@@ -33,7 +32,6 @@
import androidx.annotation.Nullable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.qs.QSTile;
@@ -42,8 +40,8 @@
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.util.leak.GarbageMonitor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -114,9 +112,6 @@
possibleTiles.add(spec);
}
}
- if (Build.IS_DEBUGGABLE && !current.contains(GarbageMonitor.MemoryTile.TILE_SPEC)) {
- possibleTiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
- }
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
possibleTiles.remove("cell");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
index 2345667..83b6f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
@@ -19,16 +19,21 @@
import android.service.quicksettings.IQSTileService;
import android.util.Log;
+import androidx.annotation.NonNull;
+
public class QSTileServiceWrapper {
private static final String TAG = "IQSTileServiceWrapper";
+ @NonNull
private final IQSTileService mService;
- public QSTileServiceWrapper(IQSTileService service) {
+ public QSTileServiceWrapper(@NonNull IQSTileService service) {
mService = service;
}
+ // This can never be null, as it's the constructor parameter and it's final
+ @NonNull
public IBinder asBinder() {
return mService.asBinder();
}
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 e08eb37..880289e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -40,6 +40,7 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -54,8 +55,10 @@
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
/**
* Manages the lifecycle of a TileService.
@@ -101,8 +104,8 @@
private final ActivityManager mActivityManager;
private Set<Integer> mQueuedMessages = new ArraySet<>();
- @Nullable
- private volatile QSTileServiceWrapper mWrapper;
+ @NonNull
+ private volatile Optional<QSTileServiceWrapper> mOptionalWrapper = Optional.empty();
private boolean mListening;
private IBinder mClickBinder;
@@ -222,6 +225,7 @@
// Only try a new binding if we are not currently bound.
mIsBound.compareAndSet(false, bindServices());
if (!mIsBound.get()) {
+ Log.d(TAG, "Failed to bind to service");
mContext.unbindService(this);
}
} catch (SecurityException e) {
@@ -281,7 +285,7 @@
service.linkToDeath(this, 0);
} catch (RemoteException e) {
}
- mWrapper = wrapper;
+ mOptionalWrapper = Optional.of(wrapper);
handlePendingMessages();
}
@@ -368,6 +372,10 @@
* are supposed to be bound, we will try to bind after some amount of time.
*/
private void handleDeath() {
+ if (!mIsBound.get()) {
+ // If we are already not bound, don't do anything else.
+ return;
+ }
mExecutor.execute(() -> {
if (!mIsBound.get()) {
// If we are already not bound, don't do anything else.
@@ -522,7 +530,7 @@
@Override
public void onTileAdded() {
if (mDebug) Log.d(TAG, "onTileAdded " + getComponent());
- if (mWrapper == null || !mWrapper.onTileAdded()) {
+ if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileAdded)) {
queueMessage(MSG_ON_ADDED);
handleDeath();
}
@@ -531,7 +539,7 @@
@Override
public void onTileRemoved() {
if (mDebug) Log.d(TAG, "onTileRemoved " + getComponent());
- if (mWrapper == null || !mWrapper.onTileRemoved()) {
+ if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onTileRemoved)) {
queueMessage(MSG_ON_REMOVED);
handleDeath();
}
@@ -541,7 +549,7 @@
public void onStartListening() {
if (mDebug) Log.d(TAG, "onStartListening " + getComponent());
mListening = true;
- if (mWrapper != null && !mWrapper.onStartListening()) {
+ if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStartListening)) {
handleDeath();
}
}
@@ -550,7 +558,7 @@
public void onStopListening() {
if (mDebug) Log.d(TAG, "onStopListening " + getComponent());
mListening = false;
- if (mWrapper != null && !mWrapper.onStopListening()) {
+ if (isNotNullAndFailedAction(mOptionalWrapper, QSTileServiceWrapper::onStopListening)) {
handleDeath();
}
}
@@ -558,7 +566,7 @@
@Override
public void onClick(IBinder iBinder) {
if (mDebug) Log.d(TAG, "onClick " + iBinder + " " + getComponent() + " " + mUser);
- if (mWrapper == null || !mWrapper.onClick(iBinder)) {
+ if (isNullOrFailedAction(mOptionalWrapper, (wrapper) -> wrapper.onClick(iBinder))) {
mClickBinder = iBinder;
queueMessage(MSG_ON_CLICK);
handleDeath();
@@ -568,7 +576,7 @@
@Override
public void onUnlockComplete() {
if (mDebug) Log.d(TAG, "onUnlockComplete " + getComponent());
- if (mWrapper == null || !mWrapper.onUnlockComplete()) {
+ if (isNullOrFailedAction(mOptionalWrapper, QSTileServiceWrapper::onUnlockComplete)) {
queueMessage(MSG_ON_UNLOCK_COMPLETE);
handleDeath();
}
@@ -577,7 +585,7 @@
@Nullable
@Override
public IBinder asBinder() {
- return mWrapper != null ? mWrapper.asBinder() : null;
+ return mOptionalWrapper.map(QSTileServiceWrapper::asBinder).orElse(null);
}
@Override
@@ -591,18 +599,42 @@
}
private void freeWrapper() {
- if (mWrapper != null) {
+ if (mOptionalWrapper.isPresent()) {
try {
- mWrapper.asBinder().unlinkToDeath(this, 0);
+ mOptionalWrapper.ifPresent(
+ (wrapper) -> wrapper.asBinder().unlinkToDeath(this, 0)
+ );
} catch (NoSuchElementException e) {
Log.w(TAG, "Trying to unlink not linked recipient for component"
+ mIntent.getComponent().flattenToShortString());
}
- mWrapper = null;
+ mOptionalWrapper = Optional.empty();
}
}
public interface TileChangeListener {
void onTileChanged(ComponentName tile);
}
+
+ /**
+ * Returns true if the Optional is empty OR performing the action on the content of the Optional
+ * (when not empty) fails.
+ */
+ private static boolean isNullOrFailedAction(
+ Optional<QSTileServiceWrapper> optionalWrapper,
+ Predicate<QSTileServiceWrapper> action
+ ) {
+ return !optionalWrapper.map(action::test).orElse(false);
+ }
+
+ /**
+ * Returns true if the Optional is not empty AND performing the action on the content of
+ * the Optional fails.
+ */
+ private static boolean isNotNullAndFailedAction(
+ Optional<QSTileServiceWrapper> optionalWrapper,
+ Predicate<QSTileServiceWrapper> action
+ ) {
+ return !optionalWrapper.map(action::test).orElse(true);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 17e6375..bdcbac0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -14,7 +14,6 @@
package com.android.systemui.qs.tileimpl;
-import android.os.Build;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -25,15 +24,14 @@
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.util.leak.GarbageMonitor;
-
-import dagger.Lazy;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
+
/**
* A factory that creates Quick Settings tiles based on a tileSpec
*
@@ -79,9 +77,7 @@
@Nullable
protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
- if (mTileMap.containsKey(tileSpec)
- // We should not return a Garbage Monitory Tile if the build is not Debuggable
- && (!tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC) || Build.IS_DEBUGGABLE)) {
+ if (mTileMap.containsKey(tileSpec)) {
return mTileMap.get(tileSpec).get();
}
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 66da8bd..216d716 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -42,9 +42,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.res.R
-import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -63,8 +61,7 @@
private val keyguardDismissUtil: KeyguardDismissUtil,
private val keyguardStateController: KeyguardStateController,
private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val sysuiDialogFactory: SystemUIDialog.Factory,
- private val userContextProvider: UserContextProvider,
+ private val delegateFactory: RecordIssueDialogDelegate.Factory,
) :
QSTileImpl<QSTile.BooleanState>(
host,
@@ -102,7 +99,8 @@
private fun showPrompt(view: View?) {
val dialog: AlertDialog =
- RecordIssueDialogDelegate(sysuiDialogFactory, userContextProvider) {
+ delegateFactory
+ .create {
isRecording = true
refreshState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index f37f58d..e89cc5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -194,7 +194,8 @@
ActivityStarter.OnDismissAction dismissAction = () -> {
if (shouldAnimateFromView) {
mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
- InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG));
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG),
+ /* animateBackgroundBoundsChange= */ true);
} else {
dialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
index 4b21e44..f071623 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -29,11 +29,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
@@ -57,7 +58,10 @@
*/
@SuppressLint("VisibleForTests") // TODO(b/260264542) Migrate away from DeviceStateManagerGlobal
@SysUISingleton
-public class RearDisplayDialogController implements CoreStartable, CommandQueue.Callbacks {
+public class RearDisplayDialogController implements
+ CoreStartable,
+ ConfigurationController.ConfigurationListener,
+ CommandQueue.Callbacks {
private int[] mFoldedStates;
private boolean mStartedFolded;
@@ -96,7 +100,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
if (mRearDisplayEducationDialog != null && mRearDisplayEducationDialog.isShowing()
&& mDialogViewContainer != null) {
// Refresh the dialog view when configuration is changed.
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt
new file mode 100644
index 0000000..6ab294d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayModule.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.reardisplay
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface RearDisplayModule {
+
+ /** Start RearDisplayDialogController. */
+ @Binds
+ @IntoMap
+ @ClassKey(RearDisplayDialogController::class)
+ abstract fun bindRearDisplayDialogControllerStartable(
+ impl: RearDisplayDialogController
+ ): CoreStartable
+
+ /** Listen to config changes for RearDisplayDialogController. */
+ @Binds
+ @IntoSet
+ fun bindRearDisplayDialogControllerConfigChanges(
+ impl: RearDisplayDialogController
+ ): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index b041f95..4ee65b9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -23,13 +23,17 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.PrintWriter;
/**
* A proxy to a Recents implementation.
*/
-public class Recents implements CoreStartable, CommandQueue.Callbacks {
+public class Recents implements
+ CoreStartable,
+ ConfigurationController.ConfigurationListener,
+ CommandQueue.Callbacks {
private final Context mContext;
private final RecentsImplementation mImpl;
@@ -53,7 +57,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
mImpl.onConfigurationChanged(newConfig);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
index 77a4b9f7..1108917 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
@@ -18,14 +18,17 @@
import android.content.Context;
-import com.android.systemui.res.R;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.ContextComponentHelper;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
/**
* Dagger injection module for {@link RecentsImplementation}
@@ -33,6 +36,28 @@
@Module
public abstract class RecentsModule {
+ /** Start Recents. */
+ @Binds
+ @IntoMap
+ @ClassKey(Recents.class)
+ abstract CoreStartable bindRecentsStartable(Recents impl);
+
+ /** Listen to config changes for Recents. */
+ @Binds
+ @IntoSet
+ abstract ConfigurationListener bindRecentsConfigChanges(Recents impl);
+
+ /** Start ScreenPinningRequest. */
+ @Binds
+ @IntoMap
+ @ClassKey(ScreenPinningRequest.class)
+ abstract CoreStartable bindScreenPinningRequestStartable(ScreenPinningRequest impl);
+
+ /** Listen to config changes for ScreenPinningRequest. */
+ @Binds
+ @IntoSet
+ abstract ConfigurationListener bindScreenPinningRequestConfigChanges(ScreenPinningRequest impl);
+
/**
* @return The {@link RecentsImplementation} from the config.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 3e574e7..2b717cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -54,25 +54,29 @@
import androidx.annotation.NonNull;
import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.leak.RotationUtils;
+import dagger.Lazy;
+
import java.util.ArrayList;
import javax.inject.Inject;
-import dagger.Lazy;
-
@SysUISingleton
-public class ScreenPinningRequest implements View.OnClickListener,
- NavigationModeController.ModeChangedListener, CoreStartable {
+public class ScreenPinningRequest implements
+ View.OnClickListener,
+ NavigationModeController.ModeChangedListener,
+ CoreStartable,
+ ConfigurationController.ConfigurationListener {
private static final String TAG = "ScreenPinningRequest";
private final Context mContext;
@@ -149,7 +153,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
if (mRequestWindow != null) {
mRequestWindow.onConfigurationChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 5bf44fa..e051df4 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -24,27 +24,63 @@
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
+import android.os.UserHandle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.WindowManager
import android.widget.Button
import android.widget.PopupMenu
import android.widget.Switch
+import androidx.annotation.AnyThread
+import androidx.annotation.MainThread
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.SessionCreationSource
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog
+import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
import com.android.systemui.screenrecord.RecordingService
import com.android.systemui.screenrecord.ScreenRecordingAudioSource
import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.Executor
-class RecordIssueDialogDelegate(
+class RecordIssueDialogDelegate
+@AssistedInject
+constructor(
private val factory: SystemUIDialog.Factory,
private val userContextProvider: UserContextProvider,
- private val onStarted: Runnable
+ private val userTracker: UserTracker,
+ private val flags: FeatureFlagsClassic,
+ @Background private val bgExecutor: Executor,
+ @Main private val mainExecutor: Executor,
+ private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>,
+ private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
+ private val userFileManager: UserFileManager,
+ @Assisted private val onStarted: Runnable,
) : SystemUIDialog.Delegate {
+ /** To inject dependencies and allow for easier testing */
+ @AssistedFactory
+ interface Factory {
+ /** Create a dialog object */
+ fun create(onStarted: Runnable): RecordIssueDialogDelegate
+ }
+
@SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch
private lateinit var issueTypeButton: Button
+ @MainThread
override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
dialog.apply {
setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null))
@@ -63,17 +99,64 @@
override fun createDialog(): SystemUIDialog = factory.create(this)
+ @MainThread
override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
dialog.apply {
window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
window?.setGravity(Gravity.CENTER)
screenRecordSwitch = requireViewById(R.id.screenrecord_switch)
+ screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled ->
+ onScreenRecordSwitchClicked(context, isEnabled)
+ }
issueTypeButton = requireViewById(R.id.issue_type_button)
issueTypeButton.setOnClickListener { onIssueTypeClicked(context) }
}
}
+ @AnyThread
+ private fun onScreenRecordSwitchClicked(context: Context, isEnabled: Boolean) {
+ if (!isEnabled) return
+
+ bgExecutor.execute {
+ if (
+ flags.isEnabled(WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES) &&
+ devicePolicyResolver
+ .get()
+ .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
+ ) {
+ mainExecutor.execute {
+ ScreenCaptureDisabledDialog(context).show()
+ screenRecordSwitch.isChecked = false
+ }
+ return@execute
+ }
+
+ mediaProjectionMetricsLogger.notifyProjectionInitiated(
+ userTracker.userId,
+ SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
+ )
+
+ if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
+ val prefs =
+ userFileManager.getSharedPreferences(
+ RecordIssueTile.TILE_SPEC,
+ Context.MODE_PRIVATE,
+ userTracker.userId
+ )
+ if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) {
+ mainExecutor.execute {
+ ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply {
+ setOnCancelListener { screenRecordSwitch.isChecked = false }
+ show()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @MainThread
private fun onIssueTypeClicked(context: Context) {
val selectedCategory = issueTypeButton.text.toString()
val popupMenu = PopupMenu(context, issueTypeButton)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
new file mode 100644
index 0000000..de6d3f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.recordissue
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
+
+class ScreenCapturePermissionDialogDelegate(
+ private val dialogFactory: SystemUIDialog.Factory,
+ private val sharedPreferences: SharedPreferences,
+) : SystemUIDialog.Delegate {
+
+ override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ dialog.apply {
+ setIcon(R.drawable.ic_screenrecord)
+ setTitle(R.string.screenrecord_permission_dialog_title)
+ setMessage(R.string.screenrecord_permission_dialog_warning_entire_screen)
+ setNegativeButton(R.string.slice_permission_deny) { _, _ -> cancel() }
+ setPositiveButton(R.string.slice_permission_allow) { _, _ ->
+ sharedPreferences.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, true).apply()
+ dismiss()
+ }
+ window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+ window?.setGravity(Gravity.CENTER)
+ }
+ }
+
+ override fun createDialog(): SystemUIDialog = dialogFactory.create(this)
+}
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 47518bb..5abb4dd 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
@@ -48,6 +48,7 @@
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
import com.android.systemui.util.println
+import dagger.Lazy
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -80,8 +81,8 @@
private val sceneLogger: SceneLogger,
@FalsingCollectorActual private val falsingCollector: FalsingCollector,
private val powerInteractor: PowerInteractor,
- private val simBouncerInteractor: SimBouncerInteractor,
- private val authenticationInteractor: AuthenticationInteractor,
+ private val simBouncerInteractor: Lazy<SimBouncerInteractor>,
+ private val authenticationInteractor: Lazy<AuthenticationInteractor>,
) : CoreStartable {
override fun start() {
@@ -152,7 +153,7 @@
}
}
applicationScope.launch {
- simBouncerInteractor.isAnySimSecure.collect { isAnySimLocked ->
+ simBouncerInteractor.get().isAnySimSecure.collect { isAnySimLocked ->
val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
val isUnlocked = deviceEntryInteractor.isUnlocked.value
@@ -166,15 +167,17 @@
isUnlocked && canSwipeToEnter == false -> {
switchToScene(
targetSceneKey = SceneKey.Gone,
- loggingReason = "All SIM cards unlocked and device already" +
- " unlocked and lockscreen doesn't require a swipe to dismiss."
+ loggingReason =
+ "All SIM cards unlocked and device already" +
+ " unlocked and lockscreen doesn't require a swipe to dismiss."
)
}
else -> {
switchToScene(
targetSceneKey = SceneKey.Lockscreen,
- loggingReason = "All SIM cards unlocked and device still locked" +
- " or lockscreen still requires a swipe to dismiss."
+ loggingReason =
+ "All SIM cards unlocked and device still locked" +
+ " or lockscreen still requires a swipe to dismiss."
)
}
}
@@ -262,7 +265,7 @@
" to swipe up on lockscreen to enter.",
)
} else if (
- authenticationInteractor.getAuthenticationMethod() ==
+ authenticationInteractor.get().getAuthenticationMethod() ==
AuthenticationMethodModel.Sim
) {
switchToScene(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index a950539..bee3152 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -29,7 +29,7 @@
import android.view.RemoteAnimationTarget
import android.view.WindowManager
import android.view.WindowManagerGlobal
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
import com.android.internal.infra.ServiceConnector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
index 5cbea90..7130fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -11,8 +11,6 @@
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.constraintlayout.widget.Guideline
import com.android.systemui.res.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import javax.inject.Inject
/**
@@ -23,7 +21,6 @@
constructor(
private val workProfileMessageController: WorkProfileMessageController,
private val screenshotDetectionController: ScreenshotDetectionController,
- private val featureFlags: FeatureFlags,
) {
private lateinit var container: ViewGroup
private lateinit var guideline: Guideline
@@ -63,10 +60,8 @@
fun onScreenshotTaken(screenshot: ScreenshotData) {
val workProfileData = workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
- var notifiedApps: List<CharSequence> = listOf()
- if (featureFlags.isEnabled(Flags.SCREENSHOT_DETECTION)) {
- notifiedApps = screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
- }
+ var notifiedApps: List<CharSequence> =
+ screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
// If work profile first run needs to show, bias towards that, otherwise show screenshot
// detection notification if needed.
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index f56f416..3081f89 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -20,7 +20,7 @@
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
import kotlinx.coroutines.CoroutineScope
import java.util.function.Consumer
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index 713ede6..86f6523 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -25,7 +25,7 @@
import com.android.systemui.shade.ShadeExpansionStateManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
import kotlinx.coroutines.withContext
/** Provides state from the main SystemUI process on behalf of the Screenshot process. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 38d00f7..238a552 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -18,7 +18,7 @@
import android.media.MediaPlayer
import android.util.Log
-import com.android.app.tracing.TraceUtils.Companion.async
+import com.android.app.tracing.coroutines.async
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.google.errorprone.annotations.CanIgnoreReturnValue
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index f6c25e0..e464fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -5,7 +5,7 @@
import android.util.Log
import android.view.Display
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.app.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.coroutines.launch
import com.android.internal.logging.UiEventLogger
import com.android.internal.util.ScreenshotRequest
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 9f416bb..f2fa0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -135,6 +135,10 @@
val filter = IntentFilter().apply {
addAction(Intent.ACTION_LOCALE_CHANGED)
addAction(Intent.ACTION_USER_INFO_CHANGED)
+ addAction(Intent.ACTION_PROFILE_ADDED)
+ addAction(Intent.ACTION_PROFILE_REMOVED)
+ addAction(Intent.ACTION_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
// These get called when a managed profile goes in or out of quiet mode.
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
@@ -157,7 +161,11 @@
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_ADDED,
Intent.ACTION_MANAGED_PROFILE_REMOVED,
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> {
+ Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
+ Intent.ACTION_PROFILE_ADDED,
+ Intent.ACTION_PROFILE_REMOVED,
+ Intent.ACTION_PROFILE_AVAILABLE,
+ Intent.ACTION_PROFILE_UNAVAILABLE -> {
handleProfilesChanged()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index d13edf0..d382b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -21,6 +21,8 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -87,6 +89,17 @@
if (mShadeInteractor.isQsExpanded().getValue()) {
finish();
}
+
+ View view = findViewById(R.id.brightness_mirror_container);
+ if (view != null) {
+ collectFlow(view, mShadeInteractor.isQsExpanded(), this::onShadeStateChange);
+ }
+ }
+
+ private void onShadeStateChange(boolean isQsExpanded) {
+ if (isQsExpanded) {
+ requestFinish();
+ }
}
private void setWindowAttributes() {
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 bc5090f..be1fa2b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -227,7 +227,7 @@
mListener.onChanged(mTracking, progress, false);
SeekableSliderEventProducer eventProducer =
mBrightnessSliderHapticPlugin.getSeekableSliderEventProducer();
- if (eventProducer != null) {
+ if (eventProducer != null && fromUser) {
eventProducer.onProgressChanged(seekBar, progress, fromUser);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 95f7c94a..286037e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1096,7 +1096,7 @@
}
@Override
- public void onPulseExpansionChanged(boolean expandingChanged) {
+ public void onPulseExpansionAmountChanged(boolean expandingChanged) {
if (mKeyguardBypassController.getBypassEnabled()) {
// Position the notifications while dragging down while pulsing
requestScrollerTopPaddingUpdate(false /* animate */);
@@ -1160,9 +1160,9 @@
// Occluded->Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
mOccludedToLockscreenTransition, mMainDispatcher);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
- collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
+ collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
collectFlow(mView,
mOccludedToLockscreenTransitionViewModel.getLockscreenTranslationY(),
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
@@ -1192,8 +1192,10 @@
mLockscreenToOccludedTransition, mMainDispatcher);
collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
- collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(),
- setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+ if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(),
+ setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
+ }
// Primary bouncer->Gone (ensures lockscreen content is not visible on successful auth)
collectFlow(mView, mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha(),
@@ -1463,6 +1465,9 @@
}
private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
+ if (migrateClocksToBlueprint()) {
+ return;
+ }
mKeyguardMediaController.attachSplitShadeContainer(container);
}
@@ -1720,9 +1725,12 @@
}
// To prevent the weather clock from overlapping with the notification shelf on AOD, we use
// the small clock here
- if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf()
- && hasVisibleNotifications() && isOnAod()) {
- return SMALL;
+ // With migrateClocksToBlueprint, weather clock will have behaviors similar to other clocks
+ if (!migrateClocksToBlueprint()) {
+ if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf()
+ && hasVisibleNotifications() && isOnAod()) {
+ return SMALL;
+ }
}
return LARGE;
}
@@ -1742,8 +1750,9 @@
} else {
layout = mNotificationContainerParent;
}
+
if (migrateClocksToBlueprint()) {
- mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered);
+ mKeyguardInteractor.setClockShouldBeCentered(mSplitShadeEnabled && shouldBeCentered);
} else {
mKeyguardStatusViewController.updateAlignment(
layout, mSplitShadeEnabled, shouldBeCentered, animate);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 3cf468f..cde2a62 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -54,13 +54,10 @@
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler;
import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder;
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.log.BouncerLogger;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.res.R;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
@@ -69,7 +66,6 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.gesture.TapGestureDetector;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -204,11 +200,7 @@
AlternateBouncerInteractor alternateBouncerInteractor,
SelectedUserInteractor selectedUserInteractor,
Lazy<JavaAdapter> javaAdapter,
- Lazy<AlternateBouncerViewModel> alternateBouncerViewModel,
- Lazy<FalsingManager> falsingManager,
- Lazy<SwipeUpAnywhereGestureHandler> swipeUpAnywhereGestureHandler,
- Lazy<TapGestureDetector> tapGestureDetector,
- Lazy<AlternateBouncerUdfpsIconViewModel> alternateBouncerUdfpsIconViewModel) {
+ Lazy<AlternateBouncerDependencies> alternateBouncerDependencies) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -250,24 +242,21 @@
messageAreaControllerFactory,
bouncerMessageInteractor,
bouncerLogger,
- featureFlagsClassic,
selectedUserInteractor);
if (DeviceEntryUdfpsRefactor.isEnabled()) {
AlternateBouncerViewBinder.bind(
mView.findViewById(R.id.alternate_bouncer),
- alternateBouncerViewModel.get(),
- falsingManager.get(),
- swipeUpAnywhereGestureHandler.get(),
- tapGestureDetector.get(),
- alternateBouncerUdfpsIconViewModel.get()
+ alternateBouncerDependencies.get()
);
javaAdapter.get().alwaysCollectFlow(
- alternateBouncerViewModel.get().getForcePluginOpen(),
- forcePluginOpen ->
+ alternateBouncerDependencies.get().getViewModel()
+ .getForcePluginOpen(),
+ forcePluginOpen ->
mNotificationShadeWindowController.setForcePluginOpen(
forcePluginOpen,
- alternateBouncerViewModel.get()
+ alternateBouncerDependencies.get()
+ .getViewModel()
)
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/startable/Dependencies.kt b/packages/SystemUI/src/com/android/systemui/startable/Dependencies.kt
new file mode 100644
index 0000000..5e57f1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/startable/Dependencies.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.startable
+
+import com.android.systemui.CoreStartable
+import kotlin.reflect.KClass
+
+/**
+ * Allows a [CoreStartable] to declare that it must be started after its dependencies.
+ *
+ * This creates a partial, topological ordering. See [com.android.systemui.SystemUIApplication] for
+ * how this ordering is enforced at runtime.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class Dependencies(vararg val value: KClass<out CoreStartable> = [])
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 93c55de..71efbab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -44,6 +44,13 @@
boolean isCurrentProfile(int userId);
+ /**
+ *
+ * @param userId user Id
+ * @return true if user profile is running.
+ */
+ boolean isProfileAvailable(int userId);
+
/** Adds a listener to be notified when the current user changes. */
void addUserChangedListener(UserChangedListener listener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 05c3839..c9df317 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.statusbar;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
@@ -22,6 +24,7 @@
import static android.os.UserHandle.USER_NULL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+import static android.app.Flags.keyguardPrivateNotifications;
import static android.os.Flags.allowPrivateProfile;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -47,7 +50,6 @@
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -149,6 +151,25 @@
new ListenerSet<>();
private final Collection<Uri> mLockScreenUris = new ArrayList<>();
+ protected final BroadcastReceiver mKeyguardReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ if (ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED.equals(action)) {
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ mKeyguardAllowingNotifications =
+ intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+ if (mCurrentUserId == getSendingUserId()) {
+ boolean changed = updateLockscreenNotificationSetting();
+ if (changed) {
+ notifyNotificationStateChanged();
+ }
+ }
+ }
+ }
+ }
+ };
protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
@Override
@@ -321,11 +342,21 @@
mLockScreenUris.add(SHOW_PRIVATE_LOCKSCREEN);
dumpManager.registerDumpable(this);
+
+ if (keyguardPrivateNotifications()) {
+ init();
+ }
}
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
+ if (!keyguardPrivateNotifications()) {
+ init();
+ }
+ }
+
+ private void init() {
mLockscreenSettingsObserver = new ContentObserver(
mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
? mBackgroundHandler
@@ -408,6 +439,11 @@
new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
? mBackgroundExecutor : null, UserHandle.ALL);
+ if (keyguardPrivateNotifications()) {
+ mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
+ new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
+ mBackgroundExecutor, UserHandle.ALL);
+ }
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -449,6 +485,10 @@
mLockscreenSettingsObserver.onChange(
false, mLockScreenUris, 0, UserHandle.of(userId));
updateDpcSettings(userId);
+
+ if (keyguardPrivateNotifications()) {
+ updateGlobalKeyguardSettings();
+ }
}
public boolean shouldShowLockscreenNotifications() {
@@ -461,6 +501,13 @@
}
}
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public boolean isProfileAvailable(int userId) {
+ synchronized (mLock) {
+ return mUserManager.isUserRunning(userId);
+ }
+ }
+
private void setShowLockscreenNotifications(boolean show) {
mShowLockscreenNotifications = show;
}
@@ -470,8 +517,12 @@
boolean allowedByDpm;
if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
- && mKeyguardAllowingNotifications;
+ if (keyguardPrivateNotifications()) {
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
+ } else {
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
+ && mKeyguardAllowingNotifications;
+ }
// If DPC never notified us about a user, that means they have no policy for the user,
// and they allow the behavior
allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
@@ -514,8 +565,13 @@
1,
userId) != 0;
mUsersUsersAllowingNotifications.put(userId, newAllowLockscreen);
- boolean keyguardChanged = updateGlobalKeyguardSettings();
- return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
+
+ if (keyguardPrivateNotifications()) {
+ return (newAllowLockscreen != originalAllowLockscreen);
+ } else {
+ boolean keyguardChanged = updateGlobalKeyguardSettings();
+ return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
+ }
}
@WorkerThread
@@ -553,8 +609,14 @@
Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
return false;
}
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle);
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
+ } else {
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle);
+ }
} else {
if (userHandle == USER_ALL) {
return true;
@@ -641,9 +703,14 @@
Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
updateDpcSettings(userHandle);
}
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle);
+ } else {
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
+ }
} else {
if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
return true;
@@ -682,7 +749,12 @@
ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE;
boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
- return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ if (keyguardPrivateNotifications()) {
+ return !mKeyguardAllowingNotifications
+ || userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ } else {
+ return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ }
}
private boolean packageHasVisibilityOverride(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 984fcad..7a2e82f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -323,9 +323,14 @@
* Update the icon dimens and drawable with current resources
*/
public void updateIconDimens() {
- reloadDimens();
- updateDrawable();
- maybeUpdateIconScaleDimens();
+ Trace.beginSection("StatusBarIconView#updateIconDimens");
+ try {
+ reloadDimens();
+ updateDrawable();
+ maybeUpdateIconScaleDimens();
+ } finally {
+ Trace.endSection();
+ }
}
private void reloadDimens() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 8d1e8d0..0c67279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -20,9 +20,9 @@
import android.view.animation.Interpolator
import androidx.annotation.VisibleForTesting
import androidx.core.animation.ObjectAnimator
-import com.android.systemui.Dumpable
import com.android.app.animation.Interpolators
import com.android.app.animation.InterpolatorsAndroidX
+import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -31,6 +31,7 @@
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.DozeParameters
@@ -206,8 +207,15 @@
val nowExpanding = isPulseExpanding()
val changed = nowExpanding != pulseExpanding
pulseExpanding = nowExpanding
- for (listener in wakeUpListeners) {
- listener.onPulseExpansionChanged(changed)
+ if (!NotificationIconContainerRefactor.isEnabled) {
+ for (listener in wakeUpListeners) {
+ listener.onPulseExpansionAmountChanged(changed)
+ }
+ }
+ if (changed) {
+ for (listener in wakeUpListeners) {
+ listener.onPulseExpandingChanged(pulseExpanding)
+ }
}
}
}
@@ -620,13 +628,20 @@
*
* @param expandingChanged if the user has started or stopped expanding
*/
- fun onPulseExpansionChanged(expandingChanged: Boolean) {}
+ @Deprecated(
+ message = "Use onPulseExpandedChanged instead.",
+ replaceWith = ReplaceWith("onPulseExpandedChanged"),
+ )
+ fun onPulseExpansionAmountChanged(expandingChanged: Boolean) {}
/**
* Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
* after the start delay, or after it ends/is cancelled.
*/
fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
+
+ /** Called whenever a pulse has started or stopped expanding. */
+ fun onPulseExpandingChanged(isPulseExpanding: Boolean) {}
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 3e9c6fb..3b48b39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,10 +3,8 @@
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.RefactorFlag
import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -42,13 +40,13 @@
/** Current top corner in pixel, based on [topRoundness] and [maxRadius] */
val topCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius
+ if (NotificationsImprovedHunAnimation.isEnabled) roundableState.topCornerRadius
else topRoundness * maxRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
val bottomCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius
+ if (NotificationsImprovedHunAnimation.isEnabled) roundableState.bottomCornerRadius
else bottomRoundness * maxRadius
/** Get and update the current radii */
@@ -318,13 +316,10 @@
internal val targetView: View,
private val roundable: Roundable,
maxRadius: Float,
- featureFlags: FeatureFlags? = null
) {
internal var maxRadius = maxRadius
private set
- internal val newHeadsUpAnim = RefactorFlag.forView(Flags.IMPROVED_HUN_ANIMATIONS, featureFlags)
-
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 70ccc4f..80ef14b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.inflation;
+import static com.android.systemui.Flags.screenshareNotificationHiding;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -243,7 +244,11 @@
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
params.setUseLowPriority(isLowPriority);
- if (mNotificationLockscreenUserManager.needsRedaction(entry)) {
+ // If screenshareNotificationHiding is enabled, both public and private views should be
+ // inflated to avoid any latency associated with reinflating all notification views when
+ // screen share starts and stops
+ if (screenshareNotificationHiding()
+ || mNotificationLockscreenUserManager.needsRedaction(entry)) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
} else {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
index cf03d1c..2cc1403 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
@@ -62,8 +62,8 @@
override val isPulseExpanding: Flow<Boolean> = conflatedCallbackFlow {
val listener =
object : NotificationWakeUpCoordinator.WakeUpListener {
- override fun onPulseExpansionChanged(expandingChanged: Boolean) {
- trySend(expandingChanged)
+ override fun onPulseExpandingChanged(isPulseExpanding: Boolean) {
+ trySend(isPulseExpanding)
}
}
trySend(wakeUpCoordinator.isPulseExpanding())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
index 30e2f0e0..b913355 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
@@ -17,19 +17,23 @@
package com.android.systemui.statusbar.notification.icon.domain.interactor
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlin.jvm.optionals.getOrNull
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
/** Domain logic related to notification icons. */
class NotificationIconsInteractor
@@ -37,10 +41,12 @@
constructor(
private val activeNotificationsInteractor: ActiveNotificationsInteractor,
private val bubbles: Optional<Bubbles>,
+ private val headsUpNotificationIconInteractor: HeadsUpNotificationIconInteractor,
private val keyguardViewStateRepository: NotificationsKeyguardViewStateRepository,
) {
/** Returns a subset of all active notifications based on the supplied filtration parameters. */
fun filteredNotifSet(
+ forceShowHeadsUp: Boolean = false,
showAmbient: Boolean = true,
showLowPriority: Boolean = true,
showDismissed: Boolean = true,
@@ -49,18 +55,21 @@
): Flow<Set<ActiveNotificationModel>> {
return combine(
activeNotificationsInteractor.topLevelRepresentativeNotifications,
+ headsUpNotificationIconInteractor.isolatedNotification,
keyguardViewStateRepository.areNotificationsFullyHidden,
- ) { notifications, notifsFullyHidden ->
+ ) { notifications, isolatedNotifKey, notifsFullyHidden ->
notifications
.asSequence()
.filter { model: ActiveNotificationModel ->
shouldShowNotificationIcon(
model = model,
+ forceShowHeadsUp = forceShowHeadsUp,
showAmbient = showAmbient,
showLowPriority = showLowPriority,
showDismissed = showDismissed,
showRepliedMessages = showRepliedMessages,
showPulsing = showPulsing,
+ isolatedNotifKey = isolatedNotifKey,
notifsFullyHidden = notifsFullyHidden,
)
}
@@ -70,14 +79,17 @@
private fun shouldShowNotificationIcon(
model: ActiveNotificationModel,
+ forceShowHeadsUp: Boolean,
showAmbient: Boolean,
showLowPriority: Boolean,
showDismissed: Boolean,
showRepliedMessages: Boolean,
showPulsing: Boolean,
+ isolatedNotifKey: String?,
notifsFullyHidden: Boolean,
): Boolean {
return when {
+ forceShowHeadsUp && model.key == isolatedNotifKey -> true
!showAmbient && model.isAmbient -> false
!showLowPriority && model.isSilent -> false
!showDismissed && model.isRowDismissed -> false
@@ -94,34 +106,41 @@
class AlwaysOnDisplayNotificationIconsInteractor
@Inject
constructor(
+ @Background bgContext: CoroutineContext,
deviceEntryInteractor: DeviceEntryInteractor,
iconsInteractor: NotificationIconsInteractor,
) {
val aodNotifs: Flow<Set<ActiveNotificationModel>> =
- deviceEntryInteractor.isBypassEnabled.flatMapLatest { isBypassEnabled ->
- iconsInteractor.filteredNotifSet(
- showAmbient = false,
- showDismissed = false,
- showRepliedMessages = false,
- showPulsing = !isBypassEnabled,
- )
- }
+ deviceEntryInteractor.isBypassEnabled
+ .flatMapLatest { isBypassEnabled ->
+ iconsInteractor.filteredNotifSet(
+ showAmbient = false,
+ showDismissed = false,
+ showRepliedMessages = false,
+ showPulsing = !isBypassEnabled,
+ )
+ }
+ .flowOn(bgContext)
}
/** Domain logic related to notification icons shown in the status bar. */
class StatusBarNotificationIconsInteractor
@Inject
constructor(
+ @Background bgContext: CoroutineContext,
iconsInteractor: NotificationIconsInteractor,
settingsRepository: NotificationListenerSettingsRepository,
) {
val statusBarNotifs: Flow<Set<ActiveNotificationModel>> =
- settingsRepository.showSilentStatusIcons.flatMapLatest { showSilentIcons ->
- iconsInteractor.filteredNotifSet(
- showAmbient = false,
- showLowPriority = showSilentIcons,
- showDismissed = false,
- showRepliedMessages = false,
- )
- }
+ settingsRepository.showSilentStatusIcons
+ .flatMapLatest { showSilentIcons ->
+ iconsInteractor.filteredNotifSet(
+ forceShowHeadsUp = true,
+ showAmbient = false,
+ showLowPriority = showSilentIcons,
+ showDismissed = false,
+ showRepliedMessages = false,
+ )
+ }
+ .flowOn(bgContext)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
new file mode 100644
index 0000000..38489f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.statusbar.notification.icon.ui.viewbinder
+
+import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.traceSection
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
+
+/** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */
+class NotificationIconContainerAlwaysOnDisplayViewBinder
+@Inject
+constructor(
+ private val viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val configuration: ConfigurationState,
+ private val failureTracker: StatusBarIconViewBindingFailureTracker,
+ private val screenOffAnimationController: ScreenOffAnimationController,
+ private val systemBarUtilsState: SystemBarUtilsState,
+ private val viewStore: AlwaysOnDisplayNotificationIconViewStore,
+) {
+ fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
+ return traceSection("NICAlwaysOnDisplay#bindWhileAttached") {
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ launch {
+ NotificationIconContainerViewBinder.bind(
+ view = view,
+ viewModel = viewModel,
+ configuration = configuration,
+ systemBarUtilsState = systemBarUtilsState,
+ failureTracker = failureTracker,
+ viewStore = viewStore,
+ )
+ }
+ launch {
+ KeyguardRootViewBinder.bindAodNotifIconVisibility(
+ view = view,
+ isVisible = keyguardRootViewModel.isNotifIconContainerVisible,
+ configuration = configuration,
+ screenOffAnimationController = screenOffAnimationController,
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+/** [IconViewStore] for the always-on display. */
+class AlwaysOnDisplayNotificationIconViewStore
+@Inject
+constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.aodIcon })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
new file mode 100644
index 0000000..783488af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.statusbar.notification.icon.ui.viewbinder
+
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.bindIcons
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import javax.inject.Inject
+
+/** Binds a [NotificationIconContainer] to a [NotificationIconContainerShelfViewModel]. */
+class NotificationIconContainerShelfViewBinder
+@Inject
+constructor(
+ private val viewModel: NotificationIconContainerShelfViewModel,
+ private val configuration: ConfigurationState,
+ private val systemBarUtilsState: SystemBarUtilsState,
+ private val failureTracker: StatusBarIconViewBindingFailureTracker,
+ private val viewStore: ShelfNotificationIconViewStore,
+) {
+ suspend fun bind(view: NotificationIconContainer) {
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ systemBarUtilsState,
+ notifyBindingFailures = { failureTracker.shelfFailures = it },
+ viewStore,
+ )
+ }
+}
+
+/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
+class ShelfNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.shelfIcon })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
new file mode 100644
index 0000000..3599f1f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.statusbar.notification.icon.ui.viewbinder
+
+import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.traceSection
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
+
+/** Binds a [NotificationIconContainer] to a [NotificationIconContainerStatusBarViewModel]. */
+class NotificationIconContainerStatusBarViewBinder
+@Inject
+constructor(
+ private val viewModel: NotificationIconContainerStatusBarViewModel,
+ private val configuration: ConfigurationState,
+ private val systemBarUtilsState: SystemBarUtilsState,
+ private val failureTracker: StatusBarIconViewBindingFailureTracker,
+ private val viewStore: StatusBarNotificationIconViewStore,
+) {
+ fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
+ return traceSection("NICStatusBar#bindWhileAttached") {
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ NotificationIconContainerViewBinder.bind(
+ view = view,
+ viewModel = viewModel,
+ configuration = configuration,
+ systemBarUtilsState = systemBarUtilsState,
+ failureTracker = failureTracker,
+ viewStore = viewStore,
+ )
+ }
+ }
+ }
+ }
+}
+
+/** [IconViewStore] for the status bar. */
+class StatusBarNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+ IconViewStore by (notifCollection.iconViewStoreBy { it.statusBarIcon })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index e1e30e1..ae77288 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -24,6 +24,8 @@
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.traceSection
+import com.android.internal.R as RInternal
import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.common.ui.ConfigurationState
@@ -35,7 +37,6 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
@@ -45,53 +46,18 @@
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
-import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** Binds a view-model to a [NotificationIconContainer]. */
object NotificationIconContainerViewBinder {
- @JvmStatic
- fun bindWhileAttached(
- view: NotificationIconContainer,
- viewModel: NotificationIconContainerShelfViewModel,
- configuration: ConfigurationState,
- systemBarUtilsState: SystemBarUtilsState,
- failureTracker: StatusBarIconViewBindingFailureTracker,
- viewStore: IconViewStore,
- ): DisposableHandle {
- return view.repeatWhenAttached {
- lifecycleScope.launch {
- viewModel.icons.bindIcons(
- view,
- configuration,
- systemBarUtilsState,
- notifyBindingFailures = { failureTracker.shelfFailures = it },
- viewStore,
- )
- }
- }
- }
-
- @JvmStatic
- fun bindWhileAttached(
- view: NotificationIconContainer,
- viewModel: NotificationIconContainerStatusBarViewModel,
- configuration: ConfigurationState,
- systemBarUtilsState: SystemBarUtilsState,
- failureTracker: StatusBarIconViewBindingFailureTracker,
- viewStore: IconViewStore,
- ): DisposableHandle =
- view.repeatWhenAttached {
- lifecycleScope.launch {
- bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore)
- }
- }
suspend fun bind(
view: NotificationIconContainer,
@@ -103,8 +69,8 @@
): Unit = coroutineScope {
launch {
val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
- val iconColors: Flow<NotificationIconColors> =
- viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
+ val iconColors: StateFlow<NotificationIconColors> =
+ viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }.stateIn(this)
viewModel.icons.bindIcons(
view,
configuration,
@@ -149,6 +115,14 @@
): Unit = coroutineScope {
view.setUseIncreasedIconScale(true)
launch {
+ // Collect state shared across all icon views, so that we are not duplicating collects
+ // for each individual icon.
+ val color: StateFlow<Int> =
+ configuration
+ .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
+ .stateIn(this)
+ val tintAlpha = viewModel.tintAlpha.stateIn(this)
+ val animsEnabled = viewModel.areIconAnimationsEnabled.stateIn(this)
viewModel.icons.bindIcons(
view,
configuration,
@@ -156,30 +130,16 @@
notifyBindingFailures = { failureTracker.aodFailures = it },
viewStore,
) { _, sbiv ->
- viewModel.bindAodStatusBarIconView(sbiv, configuration)
+ coroutineScope {
+ launch { StatusBarIconViewBinder.bindColor(sbiv, color) }
+ launch { StatusBarIconViewBinder.bindTintAlpha(sbiv, tintAlpha) }
+ launch { StatusBarIconViewBinder.bindAnimationsEnabled(sbiv, animsEnabled) }
+ }
}
}
launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
}
- private suspend fun NotificationIconContainerAlwaysOnDisplayViewModel.bindAodStatusBarIconView(
- sbiv: StatusBarIconView,
- configuration: ConfigurationState,
- ) {
- coroutineScope {
- launch {
- val color: Flow<Int> =
- configuration.getColorAttr(
- R.attr.wallpaperTextColor,
- DEFAULT_AOD_ICON_COLOR,
- )
- StatusBarIconViewBinder.bindColor(sbiv, color)
- }
- launch { StatusBarIconViewBinder.bindTintAlpha(sbiv, tintAlpha) }
- launch { StatusBarIconViewBinder.bindAnimationsEnabled(sbiv, areIconAnimationsEnabled) }
- }
- }
-
/** Binds to [NotificationIconContainer.setAnimationsEnabled] */
private suspend fun Flow<Boolean>.bindAnimationsEnabled(view: NotificationIconContainer) {
collect(view::setAnimationsEnabled)
@@ -215,28 +175,27 @@
* given `iconKey`. The parent [Job] of this coroutine will be cancelled automatically when the
* view is to be unbound.
*/
- private suspend fun Flow<NotificationIconsViewData>.bindIcons(
+ suspend fun Flow<NotificationIconsViewData>.bindIcons(
view: NotificationIconContainer,
configuration: ConfigurationState,
systemBarUtilsState: SystemBarUtilsState,
notifyBindingFailures: (Collection<String>) -> Unit,
viewStore: IconViewStore,
bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
- ) {
+ ): Unit = coroutineScope {
val iconSizeFlow: Flow<Int> =
- configuration.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_icon_size_sp,
- )
+ configuration.getDimensionPixelSize(RInternal.dimen.status_bar_icon_size_sp)
val iconHorizontalPaddingFlow: Flow<Int> =
configuration.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin)
val layoutParams: Flow<FrameLayout.LayoutParams> =
combine(iconSizeFlow, iconHorizontalPaddingFlow, systemBarUtilsState.statusBarHeight) {
- iconSize,
- iconHPadding,
- statusBarHeight,
- ->
- FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
- }
+ iconSize,
+ iconHPadding,
+ statusBarHeight,
+ ->
+ FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight)
+ }
+ .stateIn(this)
try {
bindIcons(view, layoutParams, notifyBindingFailures, viewStore, bindIcon)
} finally {
@@ -255,7 +214,7 @@
val failedBindings = mutableSetOf<String>()
val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>()
var prevIcons = NotificationIconsViewData()
- collect { iconsData: NotificationIconsViewData ->
+ collectTracingEach("NotifIconContainer#bindIcons") { iconsData: NotificationIconsViewData ->
val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
prevIcons = iconsData
@@ -269,13 +228,15 @@
for (notifKey in iconsDiff.removed) {
failedBindings.remove(notifKey)
val (child, job) = boundViewsByNotifKey.remove(notifKey) ?: continue
- view.removeView(child)
- job.cancel()
+ traceSection("removeIcon") {
+ view.removeView(child)
+ job.cancel()
+ }
}
// Add and bind.
val toAdd: Sequence<String> = iconsDiff.added.asSequence() + failedBindings.toList()
- for ((idx, notifKey) in toAdd.withIndex()) {
+ for (notifKey in toAdd) {
// Lookup the StatusBarIconView from the store.
val sbiv = viewStore.iconView(notifKey)
if (sbiv == null) {
@@ -283,27 +244,32 @@
continue
}
failedBindings.remove(notifKey)
- (sbiv.parent as? ViewGroup)?.run {
- if (this !== view) {
- Log.wtf(TAG, "StatusBarIconView($notifKey) has an unexpected parent")
+ traceSection("addIcon") {
+ (sbiv.parent as? ViewGroup)?.run {
+ if (this !== view) {
+ Log.wtf(
+ TAG,
+ "StatusBarIconView($notifKey) has an unexpected parent",
+ )
+ }
+ // If the container was re-inflated and re-bound, then SBIVs might still
+ // be attached to the prior view.
+ removeView(sbiv)
+ // The view might still be transiently added if it was just removed and
+ // added again.
+ removeTransientView(sbiv)
}
- // If the container was re-inflated and re-bound, then SBIVs might still be
- // attached to the prior view.
- removeView(sbiv)
- // The view might still be transiently added if it was just removed and
- // added again.
- removeTransientView(sbiv)
+ view.addView(sbiv)
+ boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
+ boundViewsByNotifKey[notifKey] =
+ Pair(
+ sbiv,
+ launch {
+ launch { layoutParams.collect { sbiv.layoutParams = it } }
+ bindIcon(notifKey, sbiv)
+ },
+ )
}
- view.addView(sbiv, idx)
- boundViewsByNotifKey.remove(notifKey)?.second?.cancel()
- boundViewsByNotifKey[notifKey] =
- Pair(
- sbiv,
- launch {
- launch { layoutParams.collect { sbiv.layoutParams = it } }
- bindIcon(notifKey, sbiv)
- },
- )
}
// Set the maximum number of icons to show in the container. Any icons over this
@@ -311,10 +277,10 @@
val maxIconsAmount: Int =
when (iconsData.limitType) {
LimitType.MaximumIndex -> {
- iconsData.visibleIcons
- .asSequence()
- .take(iconsData.iconLimit)
- .count { info -> info.notifKey in boundViewsByNotifKey }
+ iconsData.visibleIcons.asSequence().take(iconsData.iconLimit).count {
+ info ->
+ info.notifKey in boundViewsByNotifKey
+ }
}
LimitType.MaximumAmount -> {
iconsData.iconLimit
@@ -327,19 +293,21 @@
// Re-sort notification icons
view.changeViewPositions {
- val expectedChildren: List<StatusBarIconView> =
- iconsData.visibleIcons.mapNotNull {
- boundViewsByNotifKey[it.notifKey]?.first
+ traceSection("re-sort") {
+ val expectedChildren: List<StatusBarIconView> =
+ iconsData.visibleIcons.mapNotNull {
+ boundViewsByNotifKey[it.notifKey]?.first
+ }
+ val childCount = view.childCount
+ for (i in 0 until childCount) {
+ val actual = view.getChildAt(i)
+ val expected = expectedChildren[i]
+ if (actual === expected) {
+ continue
+ }
+ view.removeView(expected)
+ view.addView(expected, i)
}
- val childCount = view.childCount
- for (i in 0 until childCount) {
- val actual = view.getChildAt(i)
- val expected = expectedChildren[i]
- if (actual === expected) {
- continue
- }
- view.removeView(expected)
- view.addView(expected, i)
}
}
}
@@ -377,24 +345,14 @@
}
@ColorInt private const val DEFAULT_AOD_ICON_COLOR = Color.WHITE
- private const val TAG = "NotifIconContainerViewBinder"
+ private const val TAG = "NotifIconContainerViewBinder"
}
-/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
-class ShelfNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
- IconViewStore by (notifCollection.iconViewStoreBy { it.shelfIcon })
-
-/** [IconViewStore] for the always-on display. */
-class AlwaysOnDisplayNotificationIconViewStore
-@Inject
-constructor(notifCollection: NotifCollection) :
- IconViewStore by (notifCollection.iconViewStoreBy { it.aodIcon })
-
-/** [IconViewStore] for the status bar. */
-class StatusBarNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
- IconViewStore by (notifCollection.iconViewStoreBy { it.statusBarIcon })
-
-private fun NotifCollection.iconViewStoreBy(block: (IconPack) -> StatusBarIconView?) =
+/**
+ * Convenience builder for [IconViewStore] that uses [block] to extract the relevant
+ * [StatusBarIconView] from an [IconPack] stored inside of the [NotifCollection].
+ */
+fun NotifCollection.iconViewStoreBy(block: (IconPack) -> StatusBarIconView?) =
IconViewStore { key ->
getEntry(key)?.icons?.let(block)
}
@@ -410,3 +368,7 @@
/* bottom = */ top + height,
)
}
+
+private suspend fun <T> Flow<T>.collectTracingEach(tag: String, collector: (T) -> Unit) {
+ collect { traceSection(tag) { collector(it) } }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index 9cb60d5..d903f06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -17,6 +17,7 @@
import android.content.res.Resources
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -25,8 +26,10 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
@@ -35,46 +38,57 @@
class NotificationIconContainerAlwaysOnDisplayViewModel
@Inject
constructor(
+ @Background bgContext: CoroutineContext,
iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor,
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
@Main resources: Resources,
shadeInteractor: ShadeInteractor,
) {
-
private val maxIcons = resources.getInteger(R.integer.max_notif_icons_on_aod)
/** Are changes to the icon container animated? */
val areContainerChangesAnimated: Flow<Boolean> =
combine(
- shadeInteractor.isShadeTouchable,
- keyguardInteractor.isKeyguardVisible,
- ) { panelTouchesEnabled, isKeyguardVisible ->
- panelTouchesEnabled && isKeyguardVisible
- }
+ shadeInteractor.isShadeTouchable,
+ keyguardInteractor.isKeyguardVisible,
+ ) { panelTouchesEnabled, isKeyguardVisible ->
+ panelTouchesEnabled && isKeyguardVisible
+ }
+ .flowOn(bgContext)
/** Amount of a "white" tint to be applied to the icons. */
val tintAlpha: Flow<Float> =
combine(
- keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).onStart { emit(0f) },
- keyguardTransitionInteractor.transitionValue(KeyguardState.DOZING).onStart { emit(0f) },
- ) { aodAmt, dozeAmt ->
- aodAmt + dozeAmt // If transitioning between them, they should sum to 1f
- }
+ keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).onStart {
+ emit(0f)
+ },
+ keyguardTransitionInteractor.transitionValue(KeyguardState.DOZING).onStart {
+ emit(0f)
+ },
+ ) { aodAmt, dozeAmt ->
+ aodAmt + dozeAmt // If transitioning between them, they should sum to 1f
+ }
+ .flowOn(bgContext)
/** Are notification icons animated (ex: animated gif)? */
val areIconAnimationsEnabled: Flow<Boolean> =
- keyguardTransitionInteractor.isFinishedInStateWhere {
- // Don't animate icons when we're on AOD / dozing
- it != KeyguardState.AOD && it != KeyguardState.DOZING
- }
+ keyguardTransitionInteractor
+ .isFinishedInStateWhere {
+ // Don't animate icons when we're on AOD / dozing
+ it != KeyguardState.AOD && it != KeyguardState.DOZING
+ }
+ .flowOn(bgContext)
+ .onStart { emit(true) }
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
- iconsInteractor.aodNotifs.map { entries ->
- NotificationIconsViewData(
- visibleIcons = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
- iconLimit = maxIcons,
- )
- }
+ iconsInteractor.aodNotifs
+ .map { entries ->
+ NotificationIconsViewData(
+ visibleIcons = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
+ iconLimit = maxIcons,
+ )
+ }
+ .flowOn(bgContext)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
index 8484fdc..3574828 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -15,38 +15,45 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.notification.icon.domain.interactor.NotificationIconsInteractor
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
/** View-model for the overflow row of notification icons displayed in the notification shade. */
class NotificationIconContainerShelfViewModel
@Inject
constructor(
+ @Background bgContext: CoroutineContext,
interactor: NotificationIconsInteractor,
) {
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
- interactor.filteredNotifSet().map { entries ->
- var firstAmbient = 0
- val visibleKeys = buildList {
- for (entry in entries) {
- entry.toIconInfo(entry.shelfIcon)?.let { info ->
- add(info)
- // NOTE: we assume that all ambient notifications will be at the end of the
- // list
- if (!entry.isAmbient) {
- firstAmbient++
+ interactor
+ .filteredNotifSet()
+ .map { entries ->
+ var firstAmbient = 0
+ val visibleKeys = buildList {
+ for (entry in entries) {
+ entry.toIconInfo(entry.shelfIcon)?.let { info ->
+ add(info)
+ // NOTE: we assume that all ambient notifications will be at the end of
+ // the list
+ if (!entry.isAmbient) {
+ firstAmbient++
+ }
}
}
}
+ NotificationIconsViewData(
+ visibleKeys,
+ iconLimit = firstAmbient,
+ limitType = LimitType.MaximumIndex,
+ )
}
- NotificationIconsViewData(
- visibleKeys,
- iconLimit = firstAmbient,
- limitType = LimitType.MaximumIndex,
- )
- }
+ .flowOn(bgContext)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 6e5ac47..38921c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -17,12 +17,12 @@
import android.content.res.Resources
import android.graphics.Rect
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.StatusBarNotificationIconsInteractor
import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor
@@ -32,21 +32,23 @@
import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.toAnimatedValueFlow
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
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
/** View-model for the row of notification icons displayed in the status bar, */
class NotificationIconContainerStatusBarViewModel
@Inject
constructor(
+ @Background bgContext: CoroutineContext,
darkIconInteractor: DarkIconInteractor,
iconsInteractor: StatusBarNotificationIconsInteractor,
headsUpIconInteractor: HeadsUpNotificationIconInteractor,
keyguardInteractor: KeyguardInteractor,
- notificationsInteractor: ActiveNotificationsInteractor,
@Main resources: Resources,
shadeInteractor: ShadeInteractor,
) {
@@ -56,37 +58,36 @@
/** Are changes to the icon container animated? */
val animationsEnabled: Flow<Boolean> =
combine(
- shadeInteractor.isShadeTouchable,
- keyguardInteractor.isKeyguardShowing,
- ) { panelTouchesEnabled, isKeyguardShowing ->
- panelTouchesEnabled && !isKeyguardShowing
- }
+ shadeInteractor.isShadeTouchable,
+ keyguardInteractor.isKeyguardShowing,
+ ) { panelTouchesEnabled, isKeyguardShowing ->
+ panelTouchesEnabled && !isKeyguardShowing
+ }
+ .flowOn(bgContext)
/** The colors with which to display the notification icons. */
val iconColors: Flow<NotificationIconColorLookup> =
- combine(
- darkIconInteractor.tintAreas,
- darkIconInteractor.tintColor,
- // Included so that tints are re-applied after entries are changed.
- notificationsInteractor.topLevelRepresentativeNotifications,
- ) { areas, tint, _ ->
- NotificationIconColorLookup { viewBounds: Rect ->
- if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
- IconColorsImpl(tint, areas)
- } else {
- null
+ combine(darkIconInteractor.tintAreas, darkIconInteractor.tintColor) { areas, tint ->
+ NotificationIconColorLookup { viewBounds: Rect ->
+ if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
+ IconColorsImpl(tint, areas)
+ } else {
+ null
+ }
}
}
- }
+ .flowOn(bgContext)
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
- iconsInteractor.statusBarNotifs.map { entries ->
- NotificationIconsViewData(
- visibleIcons = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
- iconLimit = maxIcons,
- )
- }
+ iconsInteractor.statusBarNotifs
+ .map { entries ->
+ NotificationIconsViewData(
+ visibleIcons = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
+ iconLimit = maxIcons,
+ )
+ }
+ .flowOn(bgContext)
/** An Icon to show "isolated" in the IconContainer. */
val isolatedIcon: Flow<AnimatedValue<NotificationIconInfo?>> =
@@ -96,8 +97,9 @@
iconsViewData.visibleIcons.firstOrNull { it.notifKey == isolatedNotif }
}
}
- .pairwise(initialValue = null)
.distinctUntilChanged()
+ .flowOn(bgContext)
+ .pairwise(initialValue = null)
.sample(shadeInteractor.shadeExpansion) { (prev, iconInfo), shadeExpansion ->
val animate =
when {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 4fe05ec..fca527f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -31,7 +31,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
@@ -44,6 +43,7 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.util.DumpUtilsKt;
@@ -67,7 +67,8 @@
* The content of the view should start showing at animation progress value of
* #ALPHA_APPEAR_START_FRACTION.
*/
- private static final float ALPHA_APPEAR_START_FRACTION = .4f;
+
+ private static final float ALPHA_APPEAR_START_FRACTION = .7f;
/**
* The content should show fully with progress at #ALPHA_APPEAR_END_FRACTION
* The start of the animation is at #ALPHA_APPEAR_START_FRACTION
@@ -86,9 +87,7 @@
*/
private boolean mActivated;
- private final Interpolator mSlowOutFastInInterpolator;
private Interpolator mCurrentAppearInterpolator;
-
NotificationBackgroundView mBackgroundNormal;
private float mAnimationTranslationY;
private boolean mDrawingAppearAnimation;
@@ -116,7 +115,6 @@
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
- mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
setClipChildren(false);
setClipToPadding(false);
updateColors();
@@ -400,12 +398,16 @@
mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
targetValue = 1.0f;
} else {
- mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
+ mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE;
targetValue = 0.0f;
}
mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
targetValue);
- mAppearAnimator.setInterpolator(Interpolators.LINEAR);
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
+ mAppearAnimator.setInterpolator(mCurrentAppearInterpolator);
+ } else {
+ mAppearAnimator.setInterpolator(Interpolators.LINEAR);
+ }
mAppearAnimator.setDuration(
(long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
mAppearAnimator.addUpdateListener(animation -> {
@@ -502,8 +504,9 @@
}
private void updateAppearRect() {
- float interpolatedFraction = mCurrentAppearInterpolator.getInterpolation(
- mAppearAnimationFraction);
+ float interpolatedFraction =
+ NotificationsImprovedHunAnimation.isEnabled() ? mAppearAnimationFraction
+ : mCurrentAppearInterpolator.getInterpolation(mAppearAnimationFraction);
mAppearAnimationTranslation = (1.0f - interpolatedFraction) * mAnimationTranslationY;
final int actualHeight = getActualHeight();
float bottom = actualHeight * interpolatedFraction;
@@ -524,6 +527,7 @@
}
private float getInterpolatedAppearAnimationFraction() {
+
if (mAppearAnimationFraction >= 0) {
return mCurrentAppearInterpolator.getInterpolation(mAppearAnimationFraction);
}
@@ -569,7 +573,7 @@
@Override
public float getTopCornerRadius() {
- if (mImprovedHunAnimation.isEnabled()) {
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
return super.getTopCornerRadius();
}
@@ -579,7 +583,7 @@
@Override
public float getBottomCornerRadius() {
- if (mImprovedHunAnimation.isEnabled()) {
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
return super.getBottomCornerRadius();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5872840..31ca106 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -513,15 +513,13 @@
private void setImageViewAnimationRunning(ImageView imageView, boolean running) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
- if (drawable instanceof AnimationDrawable) {
- AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
+ if (drawable instanceof AnimationDrawable animationDrawable) {
if (running) {
animationDrawable.start();
} else {
animationDrawable.stop();
}
- } else if (drawable instanceof AnimatedVectorDrawable) {
- AnimatedVectorDrawable animationDrawable = (AnimatedVectorDrawable) drawable;
+ } else if (drawable instanceof AnimatedVectorDrawable animationDrawable) {
if (running) {
animationDrawable.start();
} else {
@@ -3439,8 +3437,7 @@
@Override
protected boolean childNeedsClipping(View child) {
- if (child instanceof NotificationContentView) {
- NotificationContentView contentView = (NotificationContentView) child;
+ if (child instanceof NotificationContentView contentView) {
if (isClippingNeeded()) {
return true;
} else if (hasRoundedCorner()
@@ -3522,8 +3519,7 @@
@Override
public void applyToView(View view) {
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row) {
if (row.isExpandAnimationRunning()) {
return;
}
@@ -3543,8 +3539,7 @@
@Override
protected void onYTranslationAnimationFinished(View view) {
super.onYTranslationAnimationFinished(view);
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row) {
if (row.isHeadsUpAnimatingAway()) {
row.setHeadsUpAnimatingAway(false);
}
@@ -3553,8 +3548,7 @@
@Override
public void animateTo(View child, AnimationProperties properties) {
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
if (row.isExpandAnimationRunning()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 2a3e69b..aefd348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,10 +28,9 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.RoundableState;
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -49,8 +48,6 @@
private float mOutlineAlpha = -1f;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
- protected final RefactorFlag mImprovedHunAnimation =
- RefactorFlag.forView(Flags.IMPROVED_HUN_ANIMATIONS);
/**
* {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -126,7 +123,7 @@
return EMPTY_PATH;
}
float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
- if (!mImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
+ if (!NotificationsImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
float overShoot = topRadius + bottomRadius - height;
float currentTopRoundness = getTopRoundness();
float currentBottomRoundness = getBottomRoundness();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 49674d6..c4d266e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -676,8 +676,7 @@
mViewState.headsUpIsVisible = false;
// handling reset for child notifications
- if (this instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) this;
+ if (this instanceof ExpandableNotificationRow row) {
List<ExpandableNotificationRow> children = row.getAttachedChildren();
if (row.isSummaryWithChildren() && children != null) {
for (ExpandableNotificationRow childRow : children) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsImprovedHunAnimation.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsImprovedHunAnimation.kt
new file mode 100644
index 0000000..16d35fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsImprovedHunAnimation.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.statusbar.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the notifications improved hun animation flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationsImprovedHunAnimation {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_IMPROVED_HUN_ANIMATION
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationsImprovedHunAnimation()
+
+ /**
+ * 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 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/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 699e140..819527e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -16,60 +16,41 @@
package com.android.systemui.statusbar.notification.shelf.ui.viewbinder
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.app.tracing.traceSection
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.NotificationShelf
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.ui.SystemBarUtilsState
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
object NotificationShelfViewBinder {
- fun bind(
+ suspend fun bind(
shelf: NotificationShelf,
viewModel: NotificationShelfViewModel,
- configuration: ConfigurationState,
- systemBarUtilsState: SystemBarUtilsState,
falsingManager: FalsingManager,
- iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
+ nicBinder: NotificationIconContainerShelfViewBinder,
notificationIconAreaController: NotificationIconAreaController,
- shelfIconViewStore: ShelfNotificationIconViewStore,
- ) {
+ ): Unit = coroutineScope {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
- if (NotificationIconContainerRefactor.isEnabled) {
- NotificationIconContainerViewBinder.bindWhileAttached(
- shelfIcons,
- viewModel.icons,
- configuration,
- systemBarUtilsState,
- iconViewBindingFailureTracker,
- shelfIconViewStore,
- )
- } else {
- notificationIconAreaController.setShelfIcons(shelfIcons)
- }
- repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.canModifyColorOfNotifications.collect(
- ::setCanModifyColorOfNotifications
- )
- }
- launch { viewModel.isClickable.collect(::setCanInteract) }
- registerViewListenersWhileAttached(shelf, viewModel)
+ traceSection("NotifShelf#bindShelfIcons") {
+ if (NotificationIconContainerRefactor.isEnabled) {
+ launch { nicBinder.bind(shelfIcons) }
+ } else {
+ notificationIconAreaController.setShelfIcons(shelfIcons)
}
}
+ launch {
+ viewModel.canModifyColorOfNotifications.collect(::setCanModifyColorOfNotifications)
+ }
+ launch { viewModel.isClickable.collect(::setCanInteract) }
+ registerViewListenersWhileAttached(shelf, viewModel)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
index 64b5b62c..5ca8b53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
@@ -18,7 +18,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.NotificationShelf
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
import javax.inject.Inject
@@ -32,7 +31,6 @@
constructor(
private val interactor: NotificationShelfInteractor,
activatableViewModel: ActivatableNotificationViewModel,
- val icons: NotificationIconContainerShelfViewModel,
) : ActivatableNotificationViewModel by activatableViewModel {
/** Is the shelf allowed to be clickable when it has content? */
val isClickable: Flow<Boolean>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 0236fc2..45b9c26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -869,8 +869,7 @@
Path clipPath = mChildClipPath;
if (clipPath != null) {
final float translation;
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow notificationRow) {
translation = notificationRow.getTranslation();
} else {
translation = child.getTranslationX();
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 85e63e5..0f640c9 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
@@ -112,6 +112,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -992,8 +993,7 @@
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE
- && child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ && child instanceof ExpandableNotificationRow row) {
if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0
&& row.getProvider().shouldShowGutsOnSnapOpen()) {
top = Math.min(top, row.getTranslationY());
@@ -1128,10 +1128,9 @@
for (int i = 0; i < n; i++) {
View view = getChildAt(i);
if (view.getVisibility() == View.GONE
- || !(view instanceof ExpandableNotificationRow)) {
+ || !(view instanceof ExpandableNotificationRow row)) {
continue;
}
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
currentIndex++;
boolean beforeSpeedBump;
if (mHighPriorityBeforeSpeedBump) {
@@ -1768,16 +1767,14 @@
}
public static boolean isPinnedHeadsUp(View v) {
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (v instanceof ExpandableNotificationRow row) {
return row.isHeadsUp() && row.isPinned();
}
return false;
}
private boolean isHeadsUp(View v) {
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (v instanceof ExpandableNotificationRow row) {
return row.isHeadsUp();
}
return false;
@@ -1819,8 +1816,7 @@
if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
- if (slidingChild instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+ if (slidingChild instanceof ExpandableNotificationRow row) {
NotificationEntry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
&& mTopHeadsUpEntry.getRow() != row
@@ -2363,8 +2359,7 @@
float rowTranslation = child.getTranslationY();
if (rowTranslation >= translationY) {
return child;
- } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ } else if (!ignoreChildren && child instanceof ExpandableNotificationRow row) {
if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
List<ExpandableNotificationRow> notificationChildren =
row.getAttachedChildren();
@@ -2885,8 +2880,7 @@
}
private void focusNextViewIfFocused(View view) {
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row) {
if (row.shouldRefocusOnDismiss()) {
View nextView = row.getChildAfterViewWhenDismissed();
if (nextView == null) {
@@ -3034,8 +3028,7 @@
}
private int getIntrinsicHeight(View view) {
- if (view instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) view;
+ if (view instanceof ExpandableView expandableView) {
return expandableView.getIntrinsicHeight();
}
return view.getHeight();
@@ -3125,8 +3118,7 @@
generateAddAnimation(child, false /* fromMoreCard */);
updateAnimationState(child);
updateChronometerForChild(child);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX);
}
@@ -3195,8 +3187,7 @@
}
private void updateAnimationState(boolean running, View child) {
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
row.setAnimationRunning(running);
}
}
@@ -3323,8 +3314,10 @@
logHunAnimationSkipped(row, "row has no viewState");
continue;
}
+ boolean shouldHunAppearFromTheBottom =
+ mStackScrollAlgorithm.shouldHunAppearFromBottom(mAmbientState, viewState);
if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
- if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
+ if (pinnedAndClosed || shouldHunAppearFromTheBottom) {
// Our custom add animation
type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
} else {
@@ -3336,6 +3329,11 @@
}
AnimationEvent event = new AnimationEvent(row, type);
event.headsUpFromBottom = onBottom;
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
+ // TODO(b/283084712) remove this with the flag and update the HUN filters at
+ // creation
+ event.filter.animateHeight = false;
+ }
mAnimationEvents.add(event);
if (SPEW) {
Log.v(TAG, "Generating HUN animation event: "
@@ -3350,11 +3348,6 @@
mAddedHeadsUpChildren.clear();
}
- private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
- return viewState.getYTranslation() + viewState.height
- >= mAmbientState.getMaxHeadsUpTranslation();
- }
-
private void generateGroupExpansionEvent() {
// Generate a group expansion/collapsing event if there is such a group at all
if (mExpandedGroupView != null) {
@@ -3391,8 +3384,7 @@
// we need to know the view after this one
float removedTranslation = child.getTranslationY();
boolean ignoreChildren = true;
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) {
removedTranslation = row.getTranslationWhenRemoved();
ignoreChildren = false;
@@ -3434,8 +3426,7 @@
private void generatePositionChangeEvents() {
for (ExpandableView child : mChildrenChangingPositions) {
Integer duration = null;
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
if (row.getEntry().isMarkedForUserTriggeredMovement()) {
duration = StackStateAnimator.ANIMATION_DURATION_PRIORITY_CHANGE;
row.getEntry().markForUserTriggeredMovement(false);
@@ -4119,8 +4110,7 @@
private void clearUserLockedViews() {
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = getChildAtIndex(i);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
row.setUserLocked(false);
}
}
@@ -4134,8 +4124,7 @@
);
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = getChildAtIndex(i);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
clearTemporaryViewsInGroup(
/* viewGroup = */ row.getChildrenContainer(),
/* reason = */ "clearTemporaryViewsInGroup(row.getChildrenContainer())"
@@ -4220,8 +4209,7 @@
}
void updateChronometerForChild(View child) {
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
row.setChronometerRunning(mIsExpanded);
}
}
@@ -4260,8 +4248,7 @@
}
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
- if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row && !onKeyguard()) {
// TODO: once we're recycling this will need to check the adapter position of the child
if (row.isUserLocked() && row != getFirstChildNotGone()) {
if (row.isSummaryWithChildren()) {
@@ -4320,8 +4307,7 @@
private void clearHeadsUpDisappearRunning() {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row) {
row.setHeadsUpAnimatingAway(false);
if (row.isSummaryWithChildren()) {
for (ExpandableNotificationRow child : row.getAttachedChildren()) {
@@ -4918,7 +4904,9 @@
*/
public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
+ mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
mStateAnimator.setHeadsUpAppearHeightBottom(height);
+ mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
requestChildrenUpdate();
}
@@ -5203,8 +5191,7 @@
}
View swipedView = mSwipeHelper.getSwipedView();
pw.println("Swiped view: " + swipedView);
- if (swipedView instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) swipedView;
+ if (swipedView instanceof ExpandableView expandableView) {
expandableView.dump(pw, args);
}
});
@@ -5287,8 +5274,7 @@
if (view instanceof SectionHeaderView && silentSectionWillBeGone) {
return true;
}
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row) {
if (isVisible(row) && includeChildInClearAll(row, selection)) {
return true;
}
@@ -5314,9 +5300,7 @@
if (shouldHideParent(view, selection)) {
viewsToHide.add(view);
}
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
-
+ if (view instanceof ExpandableNotificationRow parent) {
if (isChildrenVisible(parent)) {
for (ExpandableNotificationRow child : parent.getAttachedChildren()) {
if (isVisible(child) && includeChildInClearAll(child, selection)) {
@@ -5336,10 +5320,9 @@
for (int i = 0; i < childCount; i++) {
final View view = getChildAt(i);
- if (!(view instanceof ExpandableNotificationRow)) {
+ if (!(view instanceof ExpandableNotificationRow parent)) {
continue;
}
- ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
if (includeChildInClearAll(parent, selection)) {
viewsToRemove.add(parent);
}
@@ -5978,8 +5961,7 @@
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
mSwipeHelper.forceResetSwipeState(child);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow childRow = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow childRow) {
List<ExpandableNotificationRow> grandchildren = childRow.getAttachedChildren();
if (grandchildren != null) {
for (ExpandableNotificationRow grandchild : grandchildren) {
@@ -6265,8 +6247,7 @@
}
static boolean canChildBeDismissed(View v) {
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (v instanceof ExpandableNotificationRow row) {
if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
return false;
}
@@ -6276,8 +6257,7 @@
}
static boolean canChildBeCleared(View v) {
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (v instanceof ExpandableNotificationRow row) {
if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
return false;
}
@@ -6372,8 +6352,7 @@
/* Only ever called as a consequence of an expansion gesture in the shade. */
@Override
public void setUserExpandedChild(View v, boolean userExpanded) {
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (v instanceof ExpandableNotificationRow row) {
if (userExpanded && onKeyguard()) {
// Due to a race when locking the screen while touching, a notification may be
// expanded even after we went back to keyguard. An example of this happens if
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 d2fca8f..6f5058c 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
@@ -405,8 +405,7 @@
if (!mAllowLongPress) {
return;
}
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row) {
mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
.setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
.setType(MetricsEvent.TYPE_ACTION)
@@ -426,8 +425,7 @@
@Override
public void onMenuShown(View row) {
- if (row instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row;
+ if (row instanceof ExpandableNotificationRow notificationRow) {
mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
.setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
.setType(MetricsEvent.TYPE_ACTION));
@@ -492,10 +490,9 @@
*/
@Override
public void onChildDismissed(View view) {
- if (!(view instanceof ActivatableNotificationView)) {
+ if (!(view instanceof ActivatableNotificationView row)) {
return;
}
- ActivatableNotificationView row = (ActivatableNotificationView) view;
if (!row.isDismissed()) {
handleChildViewDismissed(view);
}
@@ -519,8 +516,7 @@
if (mView.getClearAllInProgress()) {
return;
}
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (view instanceof ExpandableNotificationRow row) {
if (row.isHeadsUp()) {
mHeadsUpManager.addSwipedOutNotification(
row.getEntry().getSbn().getKey());
@@ -551,8 +547,7 @@
ev.getY(),
true /* requireMinHeight */,
false /* ignoreDecors */);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (child instanceof ExpandableNotificationRow row) {
ExpandableNotificationRow parent = row.getNotificationParent();
if (parent != null && parent.areChildrenExpanded()
&& (parent.areGutsExposed()
@@ -582,8 +577,7 @@
@Override
public void onChildSnappedBack(View animView, float targetLeft) {
mView.onSwipeEnd();
- if (animView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
+ if (animView instanceof ExpandableNotificationRow row) {
if (row.isPinned() && !canChildBeDismissed(row)
&& row.getEntry().getSbn().getNotification().fullScreenIntent
== null) {
@@ -859,7 +853,7 @@
mGroupExpansionManager.registerGroupExpansionChangeListener(
(changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
- mViewBinder.bind(mView, this);
+ mViewBinder.bindWhileAttached(mView, this);
if (!FooterViewRefactor.isEnabled()) {
collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
@@ -1980,8 +1974,7 @@
// Check if we need to clear any snooze leavebehinds
if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
- && guts.getGutsContent() instanceof NotificationSnooze) {
- NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+ && guts.getGutsContent() instanceof NotificationSnooze ns) {
if ((ns.isExpanded() && isCancelOrUp)
|| (!horizontalSwipeWantsIt && scrollerWantsIt)) {
// If the leavebehind is expanded we clear it on the next up event, otherwise we
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 06ca9a5..664a6b6 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
@@ -37,6 +37,7 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import java.util.ArrayList;
import java.util.List;
@@ -66,12 +67,15 @@
private boolean mClipNotificationScrollToTop;
@VisibleForTesting
float mHeadsUpInset;
+ @VisibleForTesting
+ float mHeadsUpAppearStartAboveScreen;
private int mPinnedZTranslationExtra;
private float mNotificationScrimPadding;
private int mMarginBottom;
private float mQuickQsOffsetHeight;
private float mSmallCornerRadius;
private float mLargeCornerRadius;
+ private int mHeadsUpAppearHeightBottom;
public StackScrollAlgorithm(
Context context,
@@ -94,6 +98,8 @@
int statusBarHeight = SystemBarUtils.getStatusBarHeight(context);
mHeadsUpInset = statusBarHeight + res.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
+ mHeadsUpAppearStartAboveScreen = res.getDimensionPixelSize(
+ R.dimen.heads_up_appear_y_above_screen);
mPinnedZTranslationExtra = res.getDimensionPixelSize(
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
@@ -221,6 +227,25 @@
return getExpansionFractionWithoutShelf(mTempAlgorithmState, ambientState);
}
+ public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+ mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
+ }
+
+ /**
+ * If the QuickSettings is showing full screen, we want to animate the HeadsUp Notifications
+ * from the bottom of the screen.
+ *
+ * @param ambientState Current ambient state.
+ * @param viewState The state of the HUN that is being queried to appear from the bottom.
+ *
+ * @return true if the HeadsUp Notifications should appear from the bottom
+ */
+ public boolean shouldHunAppearFromBottom(AmbientState ambientState,
+ ExpandableViewState viewState) {
+ return viewState.getYTranslation() + viewState.height
+ >= ambientState.getMaxHeadsUpTranslation();
+ }
+
public static void log(String s) {
if (DEBUG) {
android.util.Log.i(TAG, s);
@@ -229,8 +254,7 @@
public static void logView(View view, String s) {
String viewString = "";
- if (view instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = ((ExpandableNotificationRow) view);
+ if (view instanceof ExpandableNotificationRow row) {
if (row.getEntry() == null) {
viewString = "ExpandableNotificationRow has null NotificationEntry";
} else {
@@ -264,8 +288,7 @@
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableView v = algorithmState.visibleChildren.get(i);
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (v instanceof ExpandableNotificationRow row) {
row.updateChildrenStates();
}
}
@@ -376,8 +399,7 @@
continue;
}
notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
- if (v instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (v instanceof ExpandableNotificationRow row) {
// handle the notGoneIndex for the children as well
List<ExpandableNotificationRow> children = row.getAttachedChildren();
@@ -508,10 +530,9 @@
private boolean hasNonClearableNotifs(StackScrollAlgorithmState algorithmState) {
for (int i = 0; i < algorithmState.visibleChildren.size(); i++) {
View child = algorithmState.visibleChildren.get(i);
- if (!(child instanceof ExpandableNotificationRow)) {
+ if (!(child instanceof ExpandableNotificationRow row)) {
continue;
}
- final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (!row.canViewBeCleared()) {
return true;
}
@@ -715,10 +736,9 @@
ExpandableNotificationRow pulsingRow = null;
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
- if (!(child instanceof ExpandableNotificationRow)) {
+ if (!(child instanceof ExpandableNotificationRow row)) {
continue;
}
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (!row.showingPulsing() || (i == 0 && ambientState.isPulseExpanding())) {
continue;
}
@@ -760,10 +780,9 @@
ExpandableNotificationRow topHeadsUpEntry = null;
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
- if (!(child instanceof ExpandableNotificationRow)) {
+ if (!(child instanceof ExpandableNotificationRow row)) {
continue;
}
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (!(row.isHeadsUp() || row.isHeadsUpAnimatingAway())) {
continue;
}
@@ -793,10 +812,16 @@
}
}
if (row.isPinned()) {
- // Make sure row yTranslation is at maximum the HUN yTranslation,
- // which accounts for AmbientState.stackTopMargin in split-shade.
- childState.setYTranslation(
- Math.max(childState.getYTranslation(), headsUpTranslation));
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
+ // Make sure row yTranslation is at the HUN yTranslation,
+ // which accounts for AmbientState.stackTopMargin in split-shade.
+ childState.setYTranslation(headsUpTranslation);
+ } else {
+ // Make sure row yTranslation is at maximum the HUN yTranslation,
+ // which accounts for AmbientState.stackTopMargin in split-shade.
+ childState.setYTranslation(
+ Math.max(childState.getYTranslation(), headsUpTranslation));
+ }
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
childState.hidden = false;
ExpandableViewState topState =
@@ -819,10 +844,22 @@
}
}
if (row.isHeadsUpAnimatingAway()) {
- // Make sure row yTranslation is at maximum the HUN yTranslation,
- // which accounts for AmbientState.stackTopMargin in split-shade.
- childState.setYTranslation(
- Math.max(childState.getYTranslation(), headsUpTranslation));
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
+ if (shouldHunAppearFromBottom(ambientState, childState)) {
+ // move to the bottom of the screen
+ childState.setYTranslation(
+ mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+ } else {
+ // move to the top of the screen
+ childState.setYTranslation(-ambientState.getStackTopMargin()
+ - mHeadsUpAppearStartAboveScreen);
+ }
+ } else {
+ // Make sure row yTranslation is at maximum the HUN yTranslation,
+ // which accounts for AmbientState.stackTopMargin in split-shade.
+ childState.setYTranslation(
+ Math.max(childState.getYTranslation(), headsUpTranslation));
+ }
// keep it visible for the animation
childState.hidden = false;
}
@@ -897,8 +934,7 @@
}
protected int getMaxAllowedChildHeight(View child) {
- if (child instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) child;
+ if (child instanceof ExpandableView expandableView) {
return expandableView.getIntrinsicHeight();
}
return child == null ? mCollapsedSize : child.getHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index e94258f..a3e0941 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK;
@@ -26,6 +27,7 @@
import android.view.View;
import com.android.app.animation.Interpolators;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
@@ -33,6 +35,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import java.util.ArrayList;
import java.util.HashSet;
@@ -68,6 +71,8 @@
private final int mGoToFullShadeAppearingTranslation;
private final int mPulsingAppearingTranslation;
+ @VisibleForTesting
+ float mHeadsUpAppearStartAboveScreen;
private final ExpandableViewState mTmpState = new ExpandableViewState();
private final AnimationProperties mAnimationProperties;
public NotificationStackScrollLayout mHostLayout;
@@ -85,21 +90,23 @@
private ValueAnimator mTopOverScrollAnimator;
private ValueAnimator mBottomOverScrollAnimator;
private int mHeadsUpAppearHeightBottom;
+ private int mStackTopMargin;
private boolean mShadeExpanded;
private ArrayList<ExpandableView> mTransientViewsToRemove = new ArrayList<>();
private NotificationShelf mShelf;
- private float mStatusBarIconLocation;
- private int[] mTmpLocation = new int[2];
private StackStateLogger mLogger;
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
+ // TODO(b/317061579) reload on configuration changes
mGoToFullShadeAppearingTranslation =
hostLayout.getContext().getResources().getDimensionPixelSize(
R.dimen.go_to_full_shade_appearing_translation);
mPulsingAppearingTranslation =
hostLayout.getContext().getResources().getDimensionPixelSize(
R.dimen.pulsing_notification_appear_translation);
+ mHeadsUpAppearStartAboveScreen = hostLayout.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen);
mAnimationProperties = new AnimationProperties() {
@Override
public AnimationFilter getAnimationFilter() {
@@ -455,8 +462,37 @@
.AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
ExpandableNotificationRow row = (ExpandableNotificationRow) event.mChangingView;
row.prepareExpansionChanged();
- } else if (event.animationType == NotificationStackScrollLayout
- .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
+ } else if (NotificationsImprovedHunAnimation.isEnabled()
+ && (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR)) {
+ mHeadsUpAppearChildren.add(changingView);
+
+ mTmpState.copyFrom(changingView.getViewState());
+ if (event.headsUpFromBottom) {
+ // start from the bottom of the screen
+ mTmpState.setYTranslation(
+ mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+ } else {
+ // start from the top of the screen
+ mTmpState.setYTranslation(
+ -mStackTopMargin - mHeadsUpAppearStartAboveScreen);
+ }
+ // set the height and the initial position
+ mTmpState.applyToView(changingView);
+ mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
+ Interpolators.FAST_OUT_SLOW_IN);
+
+ Runnable onAnimationEnd = null;
+ if (loggable) {
+ // This only captures HEADS_UP_APPEAR animations, but HUNs can appear with
+ // normal ADD animations, which would not be logged here.
+ String finalKey = key;
+ mLogger.logHUNViewAppearing(key);
+ onAnimationEnd = () -> mLogger.appearAnimationEnded(finalKey);
+ }
+ changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR,
+ /* isHeadsUpAppear= */ true, onAnimationEnd);
+ } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR) {
+ NotificationsImprovedHunAnimation.assertInLegacyMode();
// This item is added, initialize its properties.
ExpandableViewState viewState = changingView.getViewState();
mTmpState.copyFrom(viewState);
@@ -536,6 +572,10 @@
changingView.setInRemovalAnimation(true);
};
}
+ if (NotificationsImprovedHunAnimation.isEnabled()) {
+ mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
+ Interpolators.FAST_OUT_SLOW_IN_REVERSE);
+ }
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
0, 0.0f, true /* isHeadsUpAppear */,
@@ -601,6 +641,10 @@
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
}
+ public void setStackTopMargin(int stackTopMargin) {
+ mStackTopMargin = stackTopMargin;
+ }
+
public void setShadeExpanded(boolean shadeExpanded) {
mShadeExpanded = shadeExpanded;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt
index 274bf94..910b40f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt
@@ -16,29 +16,22 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import androidx.core.view.doOnDetach
-import androidx.lifecycle.lifecycleScope
-import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
-import kotlinx.coroutines.launch
/**
* Binds a [NotificationStackScrollLayoutController] to its [view model][NotificationListViewModel].
*/
object HideNotificationsBinder {
- fun bindHideList(
+ suspend fun bindHideList(
viewController: NotificationStackScrollLayoutController,
viewModel: NotificationListViewModel
) {
- viewController.view.repeatWhenAttached {
- lifecycleScope.launch {
- viewModel.hideListViewModel.shouldHideListForPerformance.collect { shouldHide ->
- viewController.bindHideState(shouldHide)
- }
- }
- }
-
viewController.view.doOnDetach { viewController.bindHideState(shouldHide = false) }
+
+ viewModel.hideListViewModel.shouldHideListForPerformance.collect { shouldHide ->
+ viewController.bindHideState(shouldHide)
+ }
}
private fun NotificationStackScrollLayoutController.bindHideState(shouldHide: Boolean) {
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 9373d49..1b36660 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
@@ -32,15 +32,14 @@
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import com.android.systemui.util.kotlin.getOrNull
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.combine
@@ -55,25 +54,27 @@
private val configuration: ConfigurationState,
private val falsingManager: FalsingManager,
private val iconAreaController: NotificationIconAreaController,
- private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
private val metricsLogger: MetricsLogger,
- private val shelfIconViewStore: ShelfNotificationIconViewStore,
- private val systemBarUtilsState: SystemBarUtilsState,
+ private val nicBinder: NotificationIconContainerShelfViewBinder,
) {
- fun bind(
+ fun bindWhileAttached(
view: NotificationStackScrollLayout,
viewController: NotificationStackScrollLayoutController
) {
- bindShelf(view)
- bindHideList(viewController, viewModel)
+ val shelf =
+ LayoutInflater.from(view.context)
+ .inflate(R.layout.status_bar_notification_shelf, view, false) as NotificationShelf
+ view.setShelf(shelf)
- if (FooterViewRefactor.isEnabled) {
- bindFooter(view)
- bindEmptyShade(view)
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ launch { bindShelf(shelf) }
+ launch { bindHideList(viewController, viewModel) }
- view.repeatWhenAttached {
- lifecycleScope.launch {
+ if (FooterViewRefactor.isEnabled) {
+ launch { bindFooter(view) }
+ launch { bindEmptyShade(view) }
viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
}
@@ -82,73 +83,57 @@
}
}
- private fun bindShelf(parentView: NotificationStackScrollLayout) {
- val shelf =
- LayoutInflater.from(parentView.context)
- .inflate(R.layout.status_bar_notification_shelf, parentView, false)
- as NotificationShelf
+ private suspend fun bindShelf(shelf: NotificationShelf) {
NotificationShelfViewBinder.bind(
shelf,
viewModel.shelf,
- configuration,
- systemBarUtilsState,
falsingManager,
- iconViewBindingFailureTracker,
+ nicBinder,
iconAreaController,
- shelfIconViewStore,
)
- parentView.setShelf(shelf)
}
- private fun bindFooter(parentView: NotificationStackScrollLayout) {
- viewModel.footer.ifPresent { footerViewModel ->
+ private suspend fun bindFooter(parentView: NotificationStackScrollLayout) {
+ viewModel.footer.getOrNull()?.let { footerViewModel ->
// The footer needs to be re-inflated every time the theme or the font size changes.
- parentView.repeatWhenAttached {
- configuration.reinflateAndBindLatest(
- R.layout.status_bar_notification_footer,
- parentView,
- attachToRoot = false,
- backgroundDispatcher,
- ) { footerView: FooterView ->
- traceSection("bind FooterView") {
- val disposableHandle =
- FooterViewBinder.bind(
- footerView,
- footerViewModel,
- clearAllNotifications = {
- metricsLogger.action(
- MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES
- )
- parentView.clearAllNotifications()
- },
- )
- parentView.setFooterView(footerView)
- return@reinflateAndBindLatest disposableHandle
- }
+ configuration.reinflateAndBindLatest(
+ R.layout.status_bar_notification_footer,
+ parentView,
+ attachToRoot = false,
+ backgroundDispatcher,
+ ) { footerView: FooterView ->
+ traceSection("bind FooterView") {
+ val disposableHandle =
+ FooterViewBinder.bind(
+ footerView,
+ footerViewModel,
+ clearAllNotifications = {
+ metricsLogger.action(
+ MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES
+ )
+ parentView.clearAllNotifications()
+ },
+ )
+ parentView.setFooterView(footerView)
+ return@reinflateAndBindLatest disposableHandle
}
}
}
}
- private fun bindEmptyShade(
- parentView: NotificationStackScrollLayout,
- ) {
- parentView.repeatWhenAttached {
- lifecycleScope.launch {
- combine(
- viewModel.shouldShowEmptyShadeView,
- viewModel.areNotificationsHiddenInShade,
- viewModel.hasFilteredOutSeenNotifications,
- ::Triple
- )
- .collect { (shouldShow, areNotifsHidden, hasFilteredNotifs) ->
- parentView.updateEmptyShadeView(
- shouldShow,
- areNotifsHidden,
- hasFilteredNotifs,
- )
- }
+ private suspend fun bindEmptyShade(parentView: NotificationStackScrollLayout) {
+ combine(
+ viewModel.shouldShowEmptyShadeView,
+ viewModel.areNotificationsHiddenInShade,
+ viewModel.hasFilteredOutSeenNotifications,
+ ::Triple
+ )
+ .collect { (shouldShow, areNotifsHidden, hasFilteredNotifs) ->
+ parentView.updateEmptyShadeView(
+ shouldShow,
+ areNotifsHidden,
+ hasFilteredNotifs,
+ )
}
- }
}
}
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 eff91e5..5ee38be 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
@@ -25,6 +25,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -71,6 +73,20 @@
KeyguardState.PRIMARY_BOUNCER
)
+ private val lockscreenToOccludedRunning =
+ keyguardTransitionInteractor
+ .transition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED)
+ .map { it.transitionState == STARTED || it.transitionState == RUNNING }
+ .distinctUntilChanged()
+ .onStart { emit(false) }
+
+ private val occludedToLockscreenRunning =
+ keyguardTransitionInteractor
+ .transition(KeyguardState.OCCLUDED, KeyguardState.LOCKSCREEN)
+ .map { it.transitionState == STARTED || it.transitionState == RUNNING }
+ .distinctUntilChanged()
+ .onStart { emit(false) }
+
val shadeCollapseFadeInComplete = MutableStateFlow(false)
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
@@ -122,7 +138,11 @@
) { isKeyguard, isShadeVisible, qsExpansion ->
isKeyguard && !(isShadeVisible || qsExpansion)
}
- .distinctUntilChanged()
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
/** Fade in only for use after the shade collapses */
val shadeCollpaseFadeIn: Flow<Boolean> =
@@ -182,26 +202,37 @@
)
val alpha: Flow<Float> =
- isOnLockscreenWithoutShade
- .flatMapLatest { isOnLockscreenWithoutShade ->
- combineTransform(
- merge(
- occludedToLockscreenTransitionViewModel.lockscreenAlpha,
- lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
- keyguardInteractor.keyguardAlpha,
- ),
- shadeCollpaseFadeIn,
- ) { alpha, shadeCollpaseFadeIn ->
- if (isOnLockscreenWithoutShade) {
- if (!shadeCollpaseFadeIn) {
- emit(alpha)
- }
+ // Due to issues with the legacy shade, some shade expansion events are sent incorrectly,
+ // such as when the shade resets. This can happen while the LOCKSCREEN<->OCCLUDED transition
+ // is running. Therefore use a series of flatmaps to prevent unwanted interruptions while
+ // those transitions are in progress. Without this, the alpha value will produce a visible
+ // flicker.
+ lockscreenToOccludedRunning.flatMapLatest { isLockscreenToOccludedRunning ->
+ if (isLockscreenToOccludedRunning) {
+ lockscreenToOccludedTransitionViewModel.lockscreenAlpha
+ } else {
+ occludedToLockscreenRunning.flatMapLatest { isOccludedToLockscreenRunning ->
+ if (isOccludedToLockscreenRunning) {
+ occludedToLockscreenTransitionViewModel.lockscreenAlpha.onStart { emit(0f) }
} else {
- emit(1f)
+ isOnLockscreenWithoutShade.flatMapLatest { isOnLockscreenWithoutShade ->
+ combineTransform(
+ keyguardInteractor.keyguardAlpha,
+ shadeCollpaseFadeIn,
+ ) { alpha, shadeCollpaseFadeIn ->
+ if (isOnLockscreenWithoutShade) {
+ if (!shadeCollpaseFadeIn) {
+ emit(alpha)
+ }
+ } else {
+ emit(1f)
+ }
+ }
+ }
}
}
}
- .distinctUntilChanged()
+ }
/**
* Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
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 97cb45a..6e8ad2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -21,13 +21,17 @@
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
@SysUISingleton
-class ConfigurationControllerImpl @Inject constructor(context: Context) : ConfigurationController {
+class ConfigurationControllerImpl @Inject constructor(
+ @Application context: Context,
+ ) : ConfigurationController {
- private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
+ private val listeners: MutableList<ConfigurationListener> = ArrayList()
private val lastConfig = Configuration()
private var density: Int = 0
private var smallestScreenWidth: Int = 0
@@ -143,14 +147,12 @@
}
}
-
-
- override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
+ override fun addCallback(listener: ConfigurationListener) {
listeners.add(listener)
listener.onDensityOrFontScaleChanged()
}
- override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
+ override fun removeCallback(listener: ConfigurationListener) {
listeners.remove(listener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
new file mode 100644
index 0000000..90ebaf2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.statusbar.phone
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import javax.inject.Inject
+
+@SysUISingleton
+class ConfigurationControllerStartable
+@Inject
+constructor(
+ private val configurationController: ConfigurationController,
+ private val listeners: Set<@JvmSuppressWildcards ConfigurationListener>
+) : CoreStartable {
+ override fun start() {
+ listeners.forEach { configurationController.addCallback(it) }
+ }
+}
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 145dbff..7cc0888 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -196,7 +196,7 @@
}
private void updateKeyguardStatusBarHeight() {
- MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+ ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) getLayoutParams();
lp.height = getStatusBarHeaderHeightKeyguard(mContext);
setLayoutParams(lp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index a62a1ed..e79f3ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -608,7 +608,7 @@
}
@Override
- public void onPulseExpansionChanged(boolean expandingChanged) {
+ public void onPulseExpansionAmountChanged(boolean expandingChanged) {
if (expandingChanged) {
updateAodIconsVisibility(true /* animate */, false /* force */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 0dabafb..f34a44a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -284,11 +284,22 @@
@Override
public String toString() {
- return "NotificationIconContainer("
- + "dozing=" + mDozing + " onLockScreen=" + mOnLockScreen
- + " overrideIconColor=" + mOverrideIconColor
- + " speedBumpIndex=" + mSpeedBumpIndex
- + " themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary) + ')';
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ return super.toString()
+ + " {"
+ + " overrideIconColor=" + mOverrideIconColor
+ + ", maxIcons=" + mMaxIcons
+ + ", isStaticLayout=" + mIsStaticLayout
+ + ", themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary)
+ + " }";
+ } else {
+ return "NotificationIconContainer("
+ + "dozing=" + mDozing + " onLockScreen=" + mOnLockScreen
+ + " overrideIconColor=" + mOverrideIconColor
+ + " speedBumpIndex=" + mSpeedBumpIndex
+ + " themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary)
+ + ')';
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
deleted file mode 100644
index 7048a78..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.drawable.Icon;
-import android.os.UserHandle;
-
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Wraps {@link com.android.internal.statusbar.StatusBarIcon} so we can still have a uniform list
- */
-public class StatusBarIconHolder {
- public static final int TYPE_ICON = 0;
- /**
- * TODO (b/249790733): address this once the new pipeline is in place
- * This type exists so that the new pipeline (see {@link MobileIconViewModel}) can be used
- * to inform the old view system about changes to the data set (the list of mobile icons). The
- * design of the new pipeline should allow for removal of this icon holder type, and obsolete
- * the need for this entire class.
- *
- * @deprecated This field only exists so the new status bar pipeline can interface with the
- * view holder system.
- */
- @Deprecated
- public static final int TYPE_MOBILE_NEW = 3;
-
- /**
- * TODO (b/238425913): address this once the new pipeline is in place
- * This type exists so that the new wifi pipeline can be used to inform the old view system
- * about the existence of the wifi icon. The design of the new pipeline should allow for removal
- * of this icon holder type, and obsolete the need for this entire class.
- *
- * @deprecated This field only exists so the new status bar pipeline can interface with the
- * view holder system.
- */
- @Deprecated
- public static final int TYPE_WIFI_NEW = 4;
-
- @IntDef({
- TYPE_ICON,
- TYPE_MOBILE_NEW,
- TYPE_WIFI_NEW
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface IconType {}
-
- private StatusBarIcon mIcon;
- private @IconType int mType = TYPE_ICON;
- private int mTag = 0;
-
- /** Returns a human-readable string representing the given type. */
- public static String getTypeString(@IconType int type) {
- switch(type) {
- case TYPE_ICON: return "ICON";
- case TYPE_MOBILE_NEW: return "MOBILE_NEW";
- case TYPE_WIFI_NEW: return "WIFI_NEW";
- default: return "UNKNOWN";
- }
- }
-
- private StatusBarIconHolder() {
- }
-
- public static StatusBarIconHolder fromIcon(StatusBarIcon icon) {
- StatusBarIconHolder wrapper = new StatusBarIconHolder();
- wrapper.mIcon = icon;
-
- return wrapper;
- }
-
- /** Creates a new holder with for the new wifi icon. */
- public static StatusBarIconHolder forNewWifiIcon() {
- StatusBarIconHolder holder = new StatusBarIconHolder();
- holder.mType = TYPE_WIFI_NEW;
- return holder;
- }
-
- /**
- * ONLY for use with the new connectivity pipeline, where we only need a subscriptionID to
- * determine icon ordering and building the correct view model
- */
- public static StatusBarIconHolder fromSubIdForModernMobileIcon(int subId) {
- StatusBarIconHolder holder = new StatusBarIconHolder();
- holder.mType = TYPE_MOBILE_NEW;
- holder.mTag = subId;
-
- return holder;
- }
-
- /**
- * Creates a new StatusBarIconHolder from a CallIndicatorIconState.
- */
- public static StatusBarIconHolder fromCallIndicatorState(
- Context context,
- CallIndicatorIconState state) {
- StatusBarIconHolder holder = new StatusBarIconHolder();
- int resId = state.isNoCalling ? state.noCallingResId : state.callStrengthResId;
- String contentDescription = state.isNoCalling
- ? state.noCallingDescription : state.callStrengthDescription;
- holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(),
- Icon.createWithResource(context, resId), 0, 0, contentDescription);
- holder.mTag = state.subId;
- return holder;
- }
-
- public @IconType int getType() {
- return mType;
- }
-
- @Nullable
- public StatusBarIcon getIcon() {
- return mIcon;
- }
-
- public void setIcon(StatusBarIcon icon) {
- mIcon = icon;
- }
-
- public boolean isVisible() {
- switch (mType) {
- case TYPE_ICON:
- return mIcon.visible;
- case TYPE_MOBILE_NEW:
- case TYPE_WIFI_NEW:
- // The new pipeline controls visibilities via the view model and view binder, so
- // this is effectively an unused return value.
- return true;
- default:
- return true;
- }
- }
-
- public void setVisible(boolean visible) {
- if (isVisible() == visible) {
- return;
- }
-
- switch (mType) {
- case TYPE_ICON:
- mIcon.visible = visible;
- break;
-
- case TYPE_MOBILE_NEW:
- case TYPE_WIFI_NEW:
- // The new pipeline controls visibilities via the view model and view binder, so
- // ignore setVisible.
- break;
- }
- }
-
- public int getTag() {
- return mTag;
- }
-
- @Override
- public String toString() {
- return "StatusBarIconHolder(type=" + getTypeString(mType)
- + " tag=" + getTag()
- + " visible=" + isVisible() + ")";
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
new file mode 100644
index 0000000..5b55a1e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import android.annotation.IntDef
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState
+
+/** Wraps [com.android.internal.statusbar.StatusBarIcon] so we can still have a uniform list */
+class StatusBarIconHolder private constructor() {
+ @IntDef(TYPE_ICON, TYPE_MOBILE_NEW, TYPE_WIFI_NEW)
+ @Retention(AnnotationRetention.SOURCE)
+ internal annotation class IconType
+
+ var icon: StatusBarIcon? = null
+
+ @IconType
+ var type = TYPE_ICON
+ private set
+
+ var tag = 0
+ private set
+
+ var isVisible: Boolean
+ get() =
+ when (type) {
+ TYPE_ICON -> icon!!.visible
+
+ // The new pipeline controls visibilities via the view model and
+ // view binder, so
+ // this is effectively an unused return value.
+ TYPE_MOBILE_NEW,
+ TYPE_WIFI_NEW -> true
+ else -> true
+ }
+ set(visible) {
+ if (isVisible == visible) {
+ return
+ }
+ when (type) {
+ TYPE_ICON -> icon!!.visible = visible
+ TYPE_MOBILE_NEW,
+ TYPE_WIFI_NEW -> {}
+ }
+ }
+
+ override fun toString(): String {
+ return ("StatusBarIconHolder(type=${getTypeString(type)}" +
+ " tag=$tag" +
+ " visible=$isVisible)")
+ }
+
+ companion object {
+ const val TYPE_ICON = 0
+
+ /**
+ * TODO (b/249790733): address this once the new pipeline is in place This type exists so
+ * that the new pipeline (see [MobileIconViewModel]) can be used to inform the old view
+ * system about changes to the data set (the list of mobile icons). The design of the new
+ * pipeline should allow for removal of this icon holder type, and obsolete the need for
+ * this entire class.
+ */
+ @Deprecated(
+ """This field only exists so the new status bar pipeline can interface with the
+ view holder system."""
+ )
+ const val TYPE_MOBILE_NEW = 3
+
+ /**
+ * TODO (b/238425913): address this once the new pipeline is in place This type exists so
+ * that the new wifi pipeline can be used to inform the old view system about the existence
+ * of the wifi icon. The design of the new pipeline should allow for removal of this icon
+ * holder type, and obsolete the need for this entire class.
+ */
+ @Deprecated(
+ """This field only exists so the new status bar pipeline can interface with the
+ view holder system."""
+ )
+ const val TYPE_WIFI_NEW = 4
+
+ /** Returns a human-readable string representing the given type. */
+ fun getTypeString(@IconType type: Int): String {
+ return when (type) {
+ TYPE_ICON -> "ICON"
+ TYPE_MOBILE_NEW -> "MOBILE_NEW"
+ TYPE_WIFI_NEW -> "WIFI_NEW"
+ else -> "UNKNOWN"
+ }
+ }
+
+ @JvmStatic
+ fun fromIcon(icon: StatusBarIcon?): StatusBarIconHolder {
+ val wrapper = StatusBarIconHolder()
+ wrapper.icon = icon
+ return wrapper
+ }
+
+ /** Creates a new holder with for the new wifi icon. */
+ @JvmStatic
+ fun forNewWifiIcon(): StatusBarIconHolder {
+ val holder = StatusBarIconHolder()
+ holder.type = TYPE_WIFI_NEW
+ return holder
+ }
+
+ /**
+ * ONLY for use with the new connectivity pipeline, where we only need a subscriptionID to
+ * determine icon ordering and building the correct view model
+ */
+ @JvmStatic
+ fun fromSubIdForModernMobileIcon(subId: Int): StatusBarIconHolder {
+ val holder = StatusBarIconHolder()
+ holder.type = TYPE_MOBILE_NEW
+ holder.tag = subId
+ return holder
+ }
+
+ /** Creates a new StatusBarIconHolder from a CallIndicatorIconState. */
+ @JvmStatic
+ fun fromCallIndicatorState(
+ context: Context,
+ state: CallIndicatorIconState,
+ ): StatusBarIconHolder {
+ val holder = StatusBarIconHolder()
+ val resId = if (state.isNoCalling) state.noCallingResId else state.callStrengthResId
+ val contentDescription =
+ if (state.isNoCalling) state.noCallingDescription else state.callStrengthDescription
+ holder.icon =
+ StatusBarIcon(
+ UserHandle.SYSTEM,
+ context.packageName,
+ Icon.createWithResource(context, resId),
+ 0,
+ 0,
+ contentDescription,
+ )
+ holder.tag = state.subId
+ return holder
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 942d186..b048da492 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar.phone.dagger;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
+import com.android.systemui.statusbar.phone.ConfigurationControllerStartable;
import dagger.Binds;
import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
/**
* Dagger Module providing {@link CentralSurfacesImpl}.
@@ -34,4 +38,12 @@
@Binds
@SysUISingleton
CentralSurfaces bindsCentralSurfaces(CentralSurfacesImpl impl);
+
+ /**
+ * Starts {@link ConfigurationControllerStartable}
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(ConfigurationControllerStartable.class)
+ CoreStartable bindConfigControllerStartable(ConfigurationControllerStartable impl);
}
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 cd99934..617e107 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
@@ -20,6 +20,7 @@
import android.database.ContentObserver;
import android.os.Bundle;
import android.os.Parcelable;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -38,7 +39,6 @@
import com.android.app.animation.InterpolatorsAndroidX;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
@@ -54,10 +54,7 @@
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-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.viewbinder.StatusBarNotificationIconViewStore;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -75,7 +72,6 @@
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker;
@@ -95,6 +91,8 @@
import kotlin.Unit;
+import kotlinx.coroutines.DisposableHandle;
+
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -151,10 +149,7 @@
private final DumpManager mDumpManager;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final NotificationIconContainerStatusBarViewModel mStatusBarIconsViewModel;
- private final ConfigurationState mConfigurationState;
- private final SystemBarUtilsState mSystemBarUtilsState;
- private final StatusBarNotificationIconViewStore mStatusBarIconViewStore;
+ private final NotificationIconContainerStatusBarViewBinder mNicViewBinder;
private final DemoModeController mDemoModeController;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -216,7 +211,7 @@
mWaitingForWindowStateChangeAfterCameraLaunch = false;
mTransitionFromLockscreenToDreamStarted = false;
};
- private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
+ private DisposableHandle mNicBindingDisposable;
@Inject
public CollapsedStatusBarFragment(
@@ -234,7 +229,7 @@
KeyguardStateController keyguardStateController,
ShadeViewController shadeViewController,
StatusBarStateController statusBarStateController,
- StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
+ NotificationIconContainerStatusBarViewBinder nicViewBinder,
CommandQueue commandQueue,
CarrierConfigTracker carrierConfigTracker,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
@@ -244,10 +239,6 @@
DumpManager dumpManager,
StatusBarWindowStateController statusBarWindowStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- NotificationIconContainerStatusBarViewModel statusBarIconsViewModel,
- ConfigurationState configurationState,
- SystemBarUtilsState systemBarUtilsState,
- StatusBarNotificationIconViewStore statusBarIconViewStore,
DemoModeController demoModeController) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mOngoingCallController = ongoingCallController;
@@ -263,7 +254,7 @@
mKeyguardStateController = keyguardStateController;
mShadeViewController = shadeViewController;
mStatusBarStateController = statusBarStateController;
- mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
+ mNicViewBinder = nicViewBinder;
mCommandQueue = commandQueue;
mCarrierConfigTracker = carrierConfigTracker;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
@@ -273,10 +264,6 @@
mDumpManager = dumpManager;
mStatusBarWindowStateController = statusBarWindowStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mStatusBarIconsViewModel = statusBarIconsViewModel;
- mConfigurationState = configurationState;
- mSystemBarUtilsState = systemBarUtilsState;
- mStatusBarIconViewStore = statusBarIconViewStore;
mDemoModeController = demoModeController;
}
@@ -455,24 +442,25 @@
mStartableStates.put(startable, Startable.State.STOPPED);
}
mDumpManager.unregisterDumpable(getClass().getSimpleName());
+ if (NotificationIconContainerRefactor.isEnabled()) {
+ if (mNicBindingDisposable != null) {
+ mNicBindingDisposable.dispose();
+ mNicBindingDisposable = null;
+ }
+ }
}
/** Initializes views related to the notification icon area. */
public void initNotificationIconArea() {
+ Trace.beginSection("CollapsedStatusBarFragment#initNotifIconArea");
ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area);
if (NotificationIconContainerRefactor.isEnabled()) {
- mNotificationIconAreaInner =
- LayoutInflater.from(getContext())
- .inflate(R.layout.notification_icon_area, notificationIconArea, true);
+ LayoutInflater.from(getContext())
+ .inflate(R.layout.notification_icon_area, notificationIconArea, true);
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
- NotificationIconContainerViewBinder.bindWhileAttached(
- notificationIcons,
- mStatusBarIconsViewModel,
- mConfigurationState,
- mSystemBarUtilsState,
- mIconViewBindingFailureTracker,
- mStatusBarIconViewStore);
+ mNotificationIconAreaInner = notificationIcons;
+ mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
} else {
mNotificationIconAreaInner =
mNotificationIconAreaController.getNotificationInnerAreaView();
@@ -484,6 +472,7 @@
}
updateNotificationIconAreaAndCallChip(/* animate= */ false);
+ Trace.endSection();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 425da5f..48bf7ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -27,6 +27,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -48,7 +49,7 @@
override val subId: Int,
startingIsCarrierMerged: Boolean,
override val tableLogBuffer: TableLogBuffer,
- subscriptionModel: StateFlow<SubscriptionModel?>,
+ subscriptionModel: Flow<SubscriptionModel?>,
private val defaultNetworkName: NetworkNameModel,
private val networkNameSeparator: String,
@Application scope: CoroutineScope,
@@ -331,7 +332,7 @@
fun build(
subId: Int,
startingIsCarrierMerged: Boolean,
- subscriptionModel: StateFlow<SubscriptionModel?>,
+ subscriptionModel: Flow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
): FullMobileConnectionRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 4fb99c24..f44401b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -96,7 +96,7 @@
class MobileConnectionRepositoryImpl(
override val subId: Int,
private val context: Context,
- subscriptionModel: StateFlow<SubscriptionModel?>,
+ subscriptionModel: Flow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
connectivityManager: ConnectivityManager,
@@ -325,8 +325,13 @@
override val cdmaRoaming: StateFlow<Boolean> =
telephonyPollingEvent
.mapLatest {
- val cdmaEri = telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber
- cdmaEri == ERI_ON || cdmaEri == ERI_FLASH
+ try {
+ val cdmaEri = telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber
+ cdmaEri == ERI_ON || cdmaEri == ERI_FLASH
+ } catch (e: UnsupportedOperationException) {
+ // Handles the same as a function call failure
+ false
+ }
}
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
@@ -448,7 +453,7 @@
fun build(
subId: Int,
mobileLogger: TableLogBuffer,
- subscriptionModel: StateFlow<SubscriptionModel?>,
+ subscriptionModel: Flow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
): MobileConnectionRepository {
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 2a510e4..a455db2 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
@@ -357,10 +357,10 @@
@VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache
- private fun subscriptionModelForSubId(subId: Int): StateFlow<SubscriptionModel?> {
- return subscriptions
- .map { list -> list.firstOrNull { model -> model.subscriptionId == subId } }
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ private fun subscriptionModelForSubId(subId: Int): Flow<SubscriptionModel?> {
+ return subscriptions.map { list ->
+ list.firstOrNull { model -> model.subscriptionId == subId }
+ }
}
private fun createRepositoryForSubId(subId: Int): FullMobileConnectionRepository {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 4864fb8..5bced93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -303,8 +303,7 @@
mEntry.mRemoteEditImeVisible = editTextRootWindowInsets != null
&& editTextRootWindowInsets.isVisible(WindowInsets.Type.ime());
if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
- // Pass null to ensure all inputs are cleared for this entry b/227115380
- mController.removeRemoteInput(mEntry, null,
+ mController.removeRemoteInput(mEntry, mToken,
/* reason= */"RemoteInputView$WindowInsetAnimation#onEnd");
}
}
@@ -536,6 +535,11 @@
if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
+ // RemoteInputView can be detached from window before IME close event in some cases like
+ // remote input view removal with notification update. As a result of this, RemoteInputView
+ // will stop ime animation updates, which results in never removing remote input. That's why
+ // we have to set mRemoteEditImeAnimatingAway false on detach to remove remote input.
+ mEntry.mRemoteEditImeAnimatingAway = false;
mController.removeRemoteInput(mEntry, mToken,
/* reason= */"RemoteInputView#onDetachedFromWindow");
mController.removeSpinning(mEntry.getKey(), mToken);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index fa0cb5c..66bf527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import android.app.AlarmManager;
+import android.app.Flags;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -191,7 +192,11 @@
@Override
public void setZen(int zen, Uri conditionId, String reason) {
- mNoMan.setZenMode(zen, conditionId, reason);
+ if (Flags.modesApi()) {
+ mNoMan.setZenMode(zen, conditionId, reason, /* fromUser= */ true);
+ } else {
+ mNoMan.setZenMode(zen, conditionId, reason);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastModule.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastModule.kt
new file mode 100644
index 0000000..6cd9993
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastModule.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.toast
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
+
+@Module
+interface ToastModule {
+ /** Starts ToastUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(ToastUI::class)
+ fun bindToastUIStartable(service: ToastUI): CoreStartable
+
+ /** Listen to config changes for ToastUI. */
+ @Binds @IntoSet fun bindToastUIConfigChanges(service: ToastUI): ConfigurationListener
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 27f8121..85a455d 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -42,6 +42,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import java.util.Objects;
@@ -51,7 +52,10 @@
* Controls display of text toasts.
*/
@SysUISingleton
-public class ToastUI implements CoreStartable, CommandQueue.Callbacks {
+public class ToastUI implements
+ CoreStartable,
+ ConfigurationController.ConfigurationListener,
+ CommandQueue.Callbacks {
// values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
@@ -187,7 +191,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
if (newConfig.orientation != mOrientation) {
mOrientation = newConfig.orientation;
if (mToast != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index e0d205f..c170eb5 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -37,6 +37,7 @@
import com.android.internal.util.UserIcons
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Flags.switchUserOnBg
import com.android.systemui.SystemUISecondaryUserService
import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -44,6 +45,7 @@
import com.android.systemui.dagger.SysUISingleton
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.keyguard.domain.interactor.KeyguardInteractor
@@ -100,6 +102,7 @@
broadcastDispatcher: BroadcastDispatcher,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val activityManager: ActivityManager,
private val refreshUsersScheduler: RefreshUsersScheduler,
private val guestUserInteractor: GuestUserInteractor,
@@ -339,7 +342,11 @@
}
.launchIn(applicationScope)
restartSecondaryService(repository.getSelectedUserInfo().id)
- keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+ applicationScope.launch {
+ withContext(mainDispatcher) {
+ keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+ }
+ }
}
fun addCallback(callback: UserCallback) {
@@ -593,10 +600,18 @@
private fun switchUser(userId: Int) {
// TODO(b/246631653): track jank and latency like in the old impl.
refreshUsersScheduler.pause()
- try {
- activityManager.switchUser(userId)
- } catch (e: RemoteException) {
- Log.e(TAG, "Couldn't switch user.", e)
+ val runnable = Runnable {
+ try {
+ activityManager.switchUser(userId)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Couldn't switch user.", e)
+ }
+ }
+
+ if (switchUserOnBg()) {
+ applicationScope.launch { withContext(backgroundDispatcher) { runnable.run() } }
+ } else {
+ runnable.run()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index 472f0ae..cf6b0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -5,9 +5,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Tracing
-import com.android.systemui.Flags.coroutineTracing
-import com.android.app.tracing.TraceUtils.Companion.coroutineTracingIsEnabled
-import com.android.app.tracing.TraceContextElement
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.CoroutineDispatcher
@@ -16,7 +14,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.plus
import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
/** Providers for various coroutines-related constructs. */
@Module
@@ -83,9 +80,6 @@
@Tracing
@SysUISingleton
fun tracingCoroutineContext(): CoroutineContext {
- return if (coroutineTracing()) {
- coroutineTracingIsEnabled = true
- TraceContextElement()
- } else EmptyCoroutineContext
+ return createCoroutineTracingContext()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
deleted file mode 100644
index 8215360..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.util.leak;
-
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.core.content.FileProvider;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-/**
- * Utility class for dumping, compressing, sending, and serving heap dump files.
- *
- * <p>Unlike the Internet, this IS a big truck you can dump something on.
- */
-public class DumpTruck {
- private static final String FILEPROVIDER_AUTHORITY = "com.android.systemui.fileprovider";
- private static final String FILEPROVIDER_PATH = "leak";
-
- private static final String TAG = "DumpTruck";
- private static final int BUFSIZ = 1024 * 1024; // 1MB
-
- private final Context context;
- private final GarbageMonitor mGarbageMonitor;
- private Uri hprofUri;
- private long rss;
- final StringBuilder body = new StringBuilder();
-
- public DumpTruck(Context context, GarbageMonitor garbageMonitor) {
- this.context = context;
- mGarbageMonitor = garbageMonitor;
- }
-
- /**
- * Capture memory for the given processes and zip them up for sharing.
- *
- * @param pids
- * @return this, for chaining
- */
- public DumpTruck captureHeaps(List<Long> pids) {
- final File dumpDir = new File(context.getCacheDir(), FILEPROVIDER_PATH);
- dumpDir.mkdirs();
- hprofUri = null;
-
- body.setLength(0);
- body.append("Build: ").append(Build.DISPLAY).append("\n\nProcesses:\n");
-
- final ArrayList<String> paths = new ArrayList<String>();
- final int myPid = android.os.Process.myPid();
-
- for (Long pidL : pids) {
- final int pid = pidL.intValue();
- body.append(" pid ").append(pid);
- GarbageMonitor.ProcessMemInfo info = mGarbageMonitor.getMemInfo(pid);
- if (info != null) {
- body.append(":")
- .append(" up=")
- .append(info.getUptime())
- .append(" rss=")
- .append(info.currentRss);
- rss = info.currentRss;
- }
- if (pid == myPid) {
- final String path =
- new File(dumpDir, String.format("heap-%d.ahprof", pid)).getPath();
- Log.v(TAG, "Dumping memory info for process " + pid + " to " + path);
- try {
- android.os.Debug.dumpHprofData(path); // will block
- paths.add(path);
- body.append(" (hprof attached)");
- } catch (IOException e) {
- Log.e(TAG, "error dumping memory:", e);
- body.append("\n** Could not dump heap: \n").append(e.toString()).append("\n");
- }
- }
- body.append("\n");
- }
-
- try {
- final String zipfile =
- new File(dumpDir, String.format("hprof-%d.zip", System.currentTimeMillis()))
- .getCanonicalPath();
- if (DumpTruck.zipUp(zipfile, paths)) {
- final File pathFile = new File(zipfile);
- hprofUri = FileProvider.getUriForFile(context, FILEPROVIDER_AUTHORITY, pathFile);
- Log.v(TAG, "Heap dump accessible at URI: " + hprofUri);
- }
- } catch (IOException e) {
- Log.e(TAG, "unable to zip up heapdumps", e);
- body.append("\n** Could not zip up files: \n").append(e.toString()).append("\n");
- }
-
- return this;
- }
-
- /**
- * Get the Uri of the current heap dump. Be sure to call captureHeaps first.
- *
- * @return Uri to the dump served by the SystemUI file provider
- */
- public Uri getDumpUri() {
- return hprofUri;
- }
-
- /**
- * Get an ACTION_SEND intent suitable for startActivity() or attaching to a Notification.
- *
- * @return share intent
- */
- public Intent createShareIntent() {
- Intent shareIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
- shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- shareIntent.putExtra(Intent.EXTRA_SUBJECT,
- String.format("SystemUI memory dump (rss=%dM)", rss / 1024));
-
- shareIntent.putExtra(Intent.EXTRA_TEXT, body.toString());
-
- if (hprofUri != null) {
- final ArrayList<Uri> uriList = new ArrayList<>();
- uriList.add(hprofUri);
- shareIntent.setType("application/zip");
- shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
-
- // 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[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
- new ClipData.Item(hprofUri));
- shareIntent.setClipData(clipdata);
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- return shareIntent;
- }
-
- private static boolean zipUp(String zipfilePath, ArrayList<String> paths) {
- try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipfilePath))) {
- final byte[] buf = new byte[BUFSIZ];
-
- for (String filename : paths) {
- try (InputStream is = new BufferedInputStream(new FileInputStream(filename))) {
- ZipEntry entry = new ZipEntry(filename);
- zos.putNextEntry(entry);
- int len;
- while (0 < (len = is.read(buf, 0, BUFSIZ))) {
- zos.write(buf, 0, len);
- }
- zos.closeEntry();
- }
- }
- return true;
- } catch (IOException e) {
- Log.e(TAG, "error zipping up profile data", e);
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
deleted file mode 100644
index de392d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.util.leak;
-
-import static android.service.quicksettings.Tile.STATE_ACTIVE;
-import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE;
-
-import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Process;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.view.View;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-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;
-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.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.concurrency.MessageRouter;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Suite of tools to periodically inspect the System UI heap and possibly prompt the user to
- * capture heap dumps and report them. Includes the implementation of the "Dump SysUI Heap"
- * quick settings tile.
- */
-@SysUISingleton
-public class GarbageMonitor implements Dumpable {
- // Feature switches
- // ================
-
- // Whether to use TrackedGarbage to trigger LeakReporter. Off by default unless you set the
- // appropriate sysprop on a userdebug device.
- public static final boolean LEAK_REPORTING_ENABLED = Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.enable_leak_reporting", false);
- public static final String FORCE_ENABLE_LEAK_REPORTING = "sysui_force_enable_leak_reporting";
-
- // Heap tracking: watch the current memory levels and update the MemoryTile if available.
- // On for all userdebug devices.
- public static final boolean HEAP_TRACKING_ENABLED = Build.IS_DEBUGGABLE;
-
- // Tell QSTileHost.java to toss this into the default tileset?
- public static final boolean ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS = true;
-
- // whether to use ActivityManager.setHeapLimit (and post a notification to the user asking
- // to dump the heap). Off by default unless you set the appropriate sysprop on userdebug
- private static final boolean ENABLE_AM_HEAP_LIMIT = Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.enable_sysui_heap_limit", false);
-
- // Tuning params
- // =============
-
- // threshold for setHeapLimit(), in KB (overrides R.integer.watch_heap_limit)
- private static final String SETTINGS_KEY_AM_HEAP_LIMIT = "systemui_am_heap_limit";
-
- private static final long GARBAGE_INSPECTION_INTERVAL =
- 15 * DateUtils.MINUTE_IN_MILLIS; // 15 min
- private static final long HEAP_TRACK_INTERVAL = 1 * DateUtils.MINUTE_IN_MILLIS; // 1 min
- private static final int HEAP_TRACK_HISTORY_LEN = 720; // 12 hours
-
- private static final int DO_GARBAGE_INSPECTION = 1000;
- private static final int DO_HEAP_TRACK = 3000;
-
- static final int GARBAGE_ALLOWANCE = 5;
-
- private static final String TAG = "GarbageMonitor";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private final MessageRouter mMessageRouter;
- private final TrackedGarbage mTrackedGarbage;
- private final LeakReporter mLeakReporter;
- private final Context mContext;
- private final DelayableExecutor mDelayableExecutor;
- private MemoryTile mQSTile;
- private final DumpTruck mDumpTruck;
-
- private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
- private final ArrayList<Long> mPids = new ArrayList<>();
-
- private long mHeapLimit;
-
- /**
- */
- @Inject
- public GarbageMonitor(
- Context context,
- @Background DelayableExecutor delayableExecutor,
- @Background MessageRouter messageRouter,
- LeakDetector leakDetector,
- LeakReporter leakReporter,
- DumpManager dumpManager) {
- mContext = context.getApplicationContext();
-
- mDelayableExecutor = delayableExecutor;
- mMessageRouter = messageRouter;
- mMessageRouter.subscribeTo(DO_GARBAGE_INSPECTION, this::doGarbageInspection);
- mMessageRouter.subscribeTo(DO_HEAP_TRACK, this::doHeapTrack);
-
- mTrackedGarbage = leakDetector.getTrackedGarbage();
- mLeakReporter = leakReporter;
-
- mDumpTruck = new DumpTruck(mContext, this);
-
- dumpManager.registerDumpable(getClass().getSimpleName(), this);
-
- if (ENABLE_AM_HEAP_LIMIT) {
- mHeapLimit = Settings.Global.getInt(context.getContentResolver(),
- SETTINGS_KEY_AM_HEAP_LIMIT,
- mContext.getResources().getInteger(R.integer.watch_heap_limit));
- }
- }
-
- public void startLeakMonitor() {
- if (mTrackedGarbage == null) {
- return;
- }
-
- mMessageRouter.sendMessage(DO_GARBAGE_INSPECTION);
- }
-
- public void startHeapTracking() {
- startTrackingProcess(
- android.os.Process.myPid(), mContext.getPackageName(), System.currentTimeMillis());
- mMessageRouter.sendMessage(DO_HEAP_TRACK);
- }
-
- private boolean gcAndCheckGarbage() {
- if (mTrackedGarbage.countOldGarbage() > GARBAGE_ALLOWANCE) {
- Runtime.getRuntime().gc();
- return true;
- }
- return false;
- }
-
- void reinspectGarbageAfterGc() {
- int count = mTrackedGarbage.countOldGarbage();
- if (count > GARBAGE_ALLOWANCE) {
- mLeakReporter.dumpLeak(count);
- }
- }
-
- public ProcessMemInfo getMemInfo(int pid) {
- return mData.get(pid);
- }
-
- public List<Long> getTrackedProcesses() {
- return mPids;
- }
-
- public void startTrackingProcess(long pid, String name, long start) {
- synchronized (mPids) {
- if (mPids.contains(pid)) return;
-
- mPids.add(pid);
- logPids();
-
- mData.put(pid, new ProcessMemInfo(pid, name, start));
- }
- }
-
- private void logPids() {
- if (DEBUG) {
- StringBuffer sb = new StringBuffer("Now tracking processes: ");
- for (int i = 0; i < mPids.size(); i++) {
- final int p = mPids.get(i).intValue();
- sb.append(" ");
- }
- Log.v(TAG, sb.toString());
- }
- }
-
- private void update() {
- synchronized (mPids) {
- for (int i = 0; i < mPids.size(); i++) {
- final int pid = mPids.get(i).intValue();
- // rssValues contains [VmRSS, RssFile, RssAnon, VmSwap].
- long[] rssValues = Process.getRss(pid);
- if (rssValues == null && rssValues.length == 0) {
- if (DEBUG) Log.e(TAG, "update: Process.getRss() didn't provide any values.");
- break;
- }
- long rss = rssValues[0];
- final ProcessMemInfo info = mData.get(pid);
- info.rss[info.head] = info.currentRss = rss;
- info.head = (info.head + 1) % info.rss.length;
- if (info.currentRss > info.max) info.max = info.currentRss;
- if (info.currentRss == 0) {
- if (DEBUG) Log.v(TAG, "update: pid " + pid + " has rss=0, it probably died");
- mData.remove(pid);
- }
- }
- for (int i = mPids.size() - 1; i >= 0; i--) {
- final long pid = mPids.get(i).intValue();
- if (mData.get(pid) == null) {
- mPids.remove(i);
- logPids();
- }
- }
- }
- if (mQSTile != null) mQSTile.update();
- }
-
- private void setTile(MemoryTile tile) {
- mQSTile = tile;
- if (tile != null) tile.update();
- }
-
- private static String formatBytes(long b) {
- String[] SUFFIXES = {"B", "K", "M", "G", "T"};
- int i;
- for (i = 0; i < SUFFIXES.length; i++) {
- if (b < 1024) break;
- b /= 1024;
- }
- return b + SUFFIXES[i];
- }
-
- private Intent dumpHprofAndGetShareIntent() {
- return mDumpTruck.captureHeaps(getTrackedProcesses()).createShareIntent();
- }
-
- @Override
- public void dump(PrintWriter pw, @Nullable String[] args) {
- pw.println("GarbageMonitor params:");
- pw.println(String.format(" mHeapLimit=%d KB", mHeapLimit));
- pw.println(String.format(" GARBAGE_INSPECTION_INTERVAL=%d (%.1f mins)",
- GARBAGE_INSPECTION_INTERVAL,
- (float) GARBAGE_INSPECTION_INTERVAL / DateUtils.MINUTE_IN_MILLIS));
- final float htiMins = HEAP_TRACK_INTERVAL / DateUtils.MINUTE_IN_MILLIS;
- pw.println(String.format(" HEAP_TRACK_INTERVAL=%d (%.1f mins)",
- HEAP_TRACK_INTERVAL,
- htiMins));
- pw.println(String.format(" HEAP_TRACK_HISTORY_LEN=%d (%.1f hr total)",
- HEAP_TRACK_HISTORY_LEN,
- (float) HEAP_TRACK_HISTORY_LEN * htiMins / 60f));
-
- pw.println("GarbageMonitor tracked processes:");
-
- for (long pid : mPids) {
- final ProcessMemInfo pmi = mData.get(pid);
- if (pmi != null) {
- pmi.dump(pw, args);
- }
- }
- }
-
-
- private static class MemoryIconDrawable extends Drawable {
- long rss, limit;
- final Drawable baseIcon;
- final Paint paint = new Paint();
- final float dp;
-
- MemoryIconDrawable(Context context) {
- baseIcon = context.getDrawable(R.drawable.ic_memory).mutate();
- dp = context.getResources().getDisplayMetrics().density;
- paint.setColor(Color.WHITE);
- }
-
- public void setRss(long rss) {
- if (rss != this.rss) {
- this.rss = rss;
- invalidateSelf();
- }
- }
-
- public void setLimit(long limit) {
- if (limit != this.limit) {
- this.limit = limit;
- invalidateSelf();
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- baseIcon.draw(canvas);
-
- if (limit > 0 && rss > 0) {
- float frac = Math.min(1f, (float) rss / limit);
-
- final Rect bounds = getBounds();
- canvas.translate(bounds.left + 8 * dp, bounds.top + 5 * dp);
- //android:pathData="M16.0,5.0l-8.0,0.0l0.0,14.0l8.0,0.0z"
- canvas.drawRect(0, 14 * dp * (1 - frac), 8 * dp + 1, 14 * dp + 1, paint);
- }
- }
-
- @Override
- public void setBounds(int left, int top, int right, int bottom) {
- super.setBounds(left, top, right, bottom);
- baseIcon.setBounds(left, top, right, bottom);
- }
-
- @Override
- public int getIntrinsicHeight() {
- return baseIcon.getIntrinsicHeight();
- }
-
- @Override
- public int getIntrinsicWidth() {
- return baseIcon.getIntrinsicWidth();
- }
-
- @Override
- public void setAlpha(int i) {
- baseIcon.setAlpha(i);
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- baseIcon.setColorFilter(colorFilter);
- paint.setColorFilter(colorFilter);
- }
-
- @Override
- public void setTint(int tint) {
- super.setTint(tint);
- baseIcon.setTint(tint);
- }
-
- @Override
- public void setTintList(ColorStateList tint) {
- super.setTintList(tint);
- baseIcon.setTintList(tint);
- }
-
- @Override
- public void setTintMode(PorterDuff.Mode tintMode) {
- super.setTintMode(tintMode);
- baseIcon.setTintMode(tintMode);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
- }
-
- private static class MemoryGraphIcon extends QSTile.Icon {
- long rss, limit;
-
- public void setRss(long rss) {
- this.rss = rss;
- }
-
- public void setHeapLimit(long limit) {
- this.limit = limit;
- }
-
- @Override
- public Drawable getDrawable(Context context) {
- final MemoryIconDrawable drawable = new MemoryIconDrawable(context);
- drawable.setRss(rss);
- drawable.setLimit(limit);
- return drawable;
- }
- }
-
- public static class MemoryTile extends QSTileImpl<QSTile.State> {
- public static final String TILE_SPEC = "dbg:mem";
-
- private final GarbageMonitor gm;
- private ProcessMemInfo pmi;
- private boolean dumpInProgress;
- private final PanelInteractor mPanelInteractor;
-
- @Inject
- public MemoryTile(
- QSHost host,
- QsEventLogger uiEventLogger,
- @Background Looper backgroundLooper,
- @Main Handler mainHandler,
- FalsingManager falsingManager,
- MetricsLogger metricsLogger,
- StatusBarStateController statusBarStateController,
- ActivityStarter activityStarter,
- QSLogger qsLogger,
- GarbageMonitor monitor,
- PanelInteractor panelInteractor
- ) {
- super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
- statusBarStateController, activityStarter, qsLogger);
- gm = monitor;
- mPanelInteractor = panelInteractor;
- }
-
- @Override
- public State newTileState() {
- return new QSTile.State();
- }
-
- @Override
- public Intent getLongClickIntent() {
- return new Intent();
- }
-
- @Override
- protected void handleClick(@Nullable View view) {
- if (dumpInProgress) return;
-
- dumpInProgress = true;
- refreshState();
- new Thread("HeapDumpThread") {
- @Override
- public void run() {
- try {
- // wait for animations & state changes
- Thread.sleep(500);
- } catch (InterruptedException ignored) { }
- final Intent shareIntent = gm.dumpHprofAndGetShareIntent();
- mHandler.post(() -> {
- dumpInProgress = false;
- refreshState();
- mPanelInteractor.collapsePanels();
- mActivityStarter.postStartActivityDismissingKeyguard(shareIntent, 0);
- });
- }
- }.start();
- }
-
- @Override
- public int getMetricsCategory() {
- return VIEW_UNKNOWN;
- }
-
- @Override
- public void handleSetListening(boolean listening) {
- super.handleSetListening(listening);
- if (gm != null) gm.setTile(listening ? this : null);
-
- final ActivityManager am = mContext.getSystemService(ActivityManager.class);
- if (listening && gm.mHeapLimit > 0) {
- am.setWatchHeapLimit(1024 * gm.mHeapLimit); // why is this in bytes?
- } else {
- am.clearWatchHeapLimit();
- }
- }
-
- @Override
- public CharSequence getTileLabel() {
- return getState().label;
- }
-
- @Override
- protected void handleUpdateState(State state, Object arg) {
- pmi = gm.getMemInfo(Process.myPid());
- final MemoryGraphIcon icon = new MemoryGraphIcon();
- icon.setHeapLimit(gm.mHeapLimit);
- state.state = dumpInProgress ? STATE_UNAVAILABLE : STATE_ACTIVE;
- state.label = dumpInProgress
- ? "Dumping..."
- : mContext.getString(R.string.heap_dump_tile_name);
- if (pmi != null) {
- icon.setRss(pmi.currentRss);
- state.secondaryLabel =
- String.format(
- "rss: %s / %s",
- formatBytes(pmi.currentRss * 1024),
- formatBytes(gm.mHeapLimit * 1024));
- } else {
- icon.setRss(0);
- state.secondaryLabel = null;
- }
- state.icon = icon;
- }
-
- public void update() {
- refreshState();
- }
-
- public long getRss() {
- return pmi != null ? pmi.currentRss : 0;
- }
-
- public long getHeapLimit() {
- return gm != null ? gm.mHeapLimit : 0;
- }
- }
-
- /** */
- public static class ProcessMemInfo implements Dumpable {
- public long pid;
- public String name;
- public long startTime;
- public long currentRss;
- public long[] rss = new long[HEAP_TRACK_HISTORY_LEN];
- public long max = 1;
- public int head = 0;
-
- public ProcessMemInfo(long pid, String name, long start) {
- this.pid = pid;
- this.name = name;
- this.startTime = start;
- }
-
- public long getUptime() {
- return System.currentTimeMillis() - startTime;
- }
-
- @Override
- public void dump(PrintWriter pw, @Nullable String[] args) {
- pw.print("{ \"pid\": ");
- pw.print(pid);
- pw.print(", \"name\": \"");
- pw.print(name.replace('"', '-'));
- pw.print("\", \"start\": ");
- pw.print(startTime);
- pw.print(", \"rss\": [");
- // write rss values starting from the oldest, which is rss[head], wrapping around to
- // rss[(head-1) % rss.length]
- for (int i = 0; i < rss.length; i++) {
- if (i > 0) pw.print(",");
- pw.print(rss[(head + i) % rss.length]);
- }
- pw.println("] }");
- }
- }
-
- /** */
- @SysUISingleton
- public static class Service implements CoreStartable, Dumpable {
- private final Context mContext;
- private final GarbageMonitor mGarbageMonitor;
-
- @Inject
- public Service(Context context, GarbageMonitor garbageMonitor) {
- mContext = context;
- mGarbageMonitor = garbageMonitor;
- }
-
- @Override
- public void start() {
- boolean forceEnable =
- Settings.Secure.getInt(
- mContext.getContentResolver(), FORCE_ENABLE_LEAK_REPORTING, 0)
- != 0;
- if (LEAK_REPORTING_ENABLED || forceEnable) {
- mGarbageMonitor.startLeakMonitor();
- }
- if (HEAP_TRACKING_ENABLED || forceEnable) {
- mGarbageMonitor.startHeapTracking();
- }
- }
-
- @Override
- public void dump(PrintWriter pw, @Nullable String[] args) {
- if (mGarbageMonitor != null) mGarbageMonitor.dump(pw, args);
- }
- }
-
- private void doGarbageInspection(int id) {
- if (gcAndCheckGarbage()) {
- mDelayableExecutor.executeDelayed(this::reinspectGarbageAfterGc, 100);
- }
-
- mMessageRouter.cancelMessages(DO_GARBAGE_INSPECTION);
- mMessageRouter.sendMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
- }
-
- private void doHeapTrack(int id) {
- update();
- mMessageRouter.cancelMessages(DO_HEAP_TRACK);
- mMessageRouter.sendMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt
deleted file mode 100644
index e975200..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.util.leak
-
-import com.android.systemui.CoreStartable
-import com.android.systemui.qs.tileimpl.QSTileImpl
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-import dagger.multibindings.StringKey
-
-@Module
-interface GarbageMonitorModule {
- /** Inject into GarbageMonitor.Service. */
- @Binds
- @IntoMap
- @ClassKey(GarbageMonitor::class)
- fun bindGarbageMonitorService(sysui: GarbageMonitor.Service): CoreStartable
-
- @Binds
- @IntoMap
- @StringKey(GarbageMonitor.MemoryTile.TILE_SPEC)
- fun bindMemoryTile(memoryTile: GarbageMonitor.MemoryTile): QSTileImpl<*>
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 3451ae0..dc2b80c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -22,16 +22,17 @@
import android.util.Log;
import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.PrintWriter;
import javax.inject.Inject;
@SysUISingleton
-public class VolumeUI implements CoreStartable {
+public class VolumeUI implements CoreStartable, ConfigurationController.ConfigurationListener {
private static final String TAG = "VolumeUI";
private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
@@ -60,7 +61,7 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
if (!mEnabled) return;
mVolumeComponent.onConfigurationChanged(newConfig);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 53217d4..8d06a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -21,6 +21,7 @@
import android.os.Looper;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
@@ -36,15 +37,30 @@
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogImpl;
import com.android.systemui.volume.VolumePanelFactory;
+import com.android.systemui.volume.VolumeUI;
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
/** Dagger Module for code in the volume package. */
@Module
public interface VolumeModule {
+ /** Starts VolumeUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(VolumeUI.class)
+ CoreStartable bindVolumeUIStartable(VolumeUI impl);
+
+ /** Listen to config changes for VolumeUI. */
+ @Binds
+ @IntoSet
+ ConfigurationController.ConfigurationListener bindVolumeUIConfigChanges(VolumeUI impl);
+
/** */
@Binds
VolumeComponent provideVolumeComponent(VolumeDialogComponent volumeDialogComponent);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 3f76d30..8858d13 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -51,6 +51,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import dagger.Lazy
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
@@ -60,6 +61,7 @@
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var lazyKeyguardUpdateMonitor: Lazy<KeyguardUpdateMonitor>
@Mock private lateinit var mockPrintWriter: PrintWriter
@Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
@@ -72,6 +74,7 @@
MockitoAnnotations.initMocks(this)
whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(currentUser)
+ whenever(lazyKeyguardUpdateMonitor.get()).thenReturn(keyguardUpdateMonitor)
secureSettings = FakeSettings()
activeUnlockConfig =
ActiveUnlockConfig(
@@ -79,6 +82,7 @@
secureSettings,
contentResolver,
selectedUserInteractor,
+ lazyKeyguardUpdateMonitor,
dumpManager
)
}
@@ -260,7 +264,6 @@
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are NOT enrolled
- activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
`when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false)
`when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false)
@@ -290,7 +293,6 @@
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are both enrolled
- activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
`when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
`when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index 9ce9bd6..08c1de1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -55,10 +55,10 @@
import android.text.TextUtils;
import com.android.keyguard.logging.CarrierTextManagerLogger;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.LogBufferHelperKt;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.log.LogBufferHelperKt;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository;
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
import com.android.systemui.telephony.TelephonyListenerManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 6f58bc2..e6637e6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -30,13 +30,15 @@
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.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.plugins.clocks.ClockAnimations
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockTickRate
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -94,9 +96,9 @@
@Mock private lateinit var largeClockEvents: ClockFaceEvents
@Mock private lateinit var parentView: View
private lateinit var repository: FakeKeyguardRepository
- @Mock private lateinit var smallLogBuffer: LogBuffer
- @Mock private lateinit var largeLogBuffer: LogBuffer
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
+ private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
private lateinit var underTest: ClockEventController
@Mock private lateinit var zenModeController: ZenModeController
@@ -140,8 +142,7 @@
context,
mainExecutor,
bgExecutor,
- smallLogBuffer,
- largeLogBuffer,
+ clockBuffers,
withDeps.featureFlags,
zenModeController
)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 7f20d9a..51ceda3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -37,11 +37,11 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -98,7 +98,6 @@
.thenReturn(mKeyguardMessageArea);
when(mAbsKeyInputView.getResources()).thenReturn(getContext().getResources());
mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false);
mKeyguardAbsKeyInputViewController = createTestObject();
mKeyguardAbsKeyInputViewController.init();
reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
@@ -127,7 +126,7 @@
@Test
public void withFeatureFlagOn_oldMessage_isHidden() {
- mFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true);
+ mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
KeyguardAbsKeyInputViewController underTest = createTestObject();
underTest.init();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 88f63ad..a249961 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -34,14 +34,12 @@
import android.widget.RelativeLayout;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.plugins.clocks.ClockAnimations;
import com.android.systemui.plugins.clocks.ClockController;
@@ -56,14 +54,9 @@
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
-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.phone.DozeParameters;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -185,9 +178,7 @@
mKeyguardSliceViewController,
mNotificationIconAreaController,
mSmartspaceController,
- mock(SystemBarUtilsState.class),
- mock(ScreenOffAnimationController.class),
- mock(StatusBarIconViewBindingFailureTracker.class),
+ mock(NotificationIconContainerAlwaysOnDisplayViewBinder.class),
mKeyguardUnlockAnimationController,
mSecureSettings,
mExecutor,
@@ -195,11 +186,6 @@
mDumpManager,
mClockEventController,
mLogBuffer,
- mock(NotificationIconContainerAlwaysOnDisplayViewModel.class),
- mock(KeyguardRootViewModel.class),
- mock(ConfigurationState.class),
- mock(DozeParameters.class),
- mock(AlwaysOnDisplayNotificationIconViewStore.class),
KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(),
mKeyguardClockInteractor,
mFakeFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1ab634c..d03a898 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2283,6 +2283,7 @@
Optional.of(mInteractiveToAuthProvider),
mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
+ start();
}
public boolean hasSimStateJustChanged() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
index 2d5c2ab..342494d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
@@ -26,8 +26,8 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.biometrics.AuthController
import com.android.systemui.decor.FaceScanningProviderFactory
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.log.ScreenDecorationsLogger
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 639276e..c094df5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -21,7 +21,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.google.common.truth.Truth.assertThat;
@@ -248,8 +248,8 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
+ public void onConfigChanged(Configuration newConfig) {
+ super.onConfigChanged(newConfig);
mExecutor.runAllReady();
}
@@ -892,7 +892,7 @@
// Switch to long edge cutout(left).
mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
- mScreenDecorations.onConfigurationChanged(new Configuration());
+ mScreenDecorations.onConfigChanged(new Configuration());
verifyOverlaysExistAndAdded(true, false, false, false, View.VISIBLE);
}
@@ -913,7 +913,7 @@
// Switch to long edge cutout(left).
mMockCutoutList.set(0, new CutoutDecorProviderImpl(BOUNDS_POSITION_LEFT));
- mScreenDecorations.onConfigurationChanged(new Configuration());
+ mScreenDecorations.onConfigChanged(new Configuration());
verifyOverlaysExistAndAdded(true, false, true, false, View.VISIBLE);
verify(mDotViewController, times(2)).initialize(any(), any(), any(), any());
verify(mDotViewController, times(2)).setShowingListener(null);
@@ -949,7 +949,7 @@
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
- mScreenDecorations.onConfigurationChanged(new Configuration());
+ mScreenDecorations.onConfigChanged(new Configuration());
// Only top windows should be added.
verifyOverlaysExistAndAdded(false, true, false, false, View.VISIBLE);
@@ -976,7 +976,7 @@
// top cutout
mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
- mScreenDecorations.onConfigurationChanged(new Configuration());
+ mScreenDecorations.onConfigChanged(new Configuration());
// Both top and bottom windows should be added with VISIBLE because of privacy dot and
// cutout, but rounded corners visibility shall be gone because of no rounding.
@@ -1013,7 +1013,7 @@
doReturn(2f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
mDisplayInfo.rotation = Surface.ROTATION_270;
- mScreenDecorations.onConfigurationChanged(null);
+ mScreenDecorations.onConfigChanged(null);
assertEquals(new Size(6, 6), resDelegate.getTopRoundedSize());
assertEquals(new Size(8, 8), resDelegate.getBottomRoundedSize());
@@ -1145,7 +1145,7 @@
assertThat(mScreenDecorations.mIsRegistered, is(false));
doReturn(true).when(mScreenDecorations).hasOverlays();
- mScreenDecorations.onConfigurationChanged(new Configuration());
+ mScreenDecorations.onConfigChanged(new Configuration());
assertThat(mScreenDecorations.mIsRegistered, is(true));
}
@@ -1156,7 +1156,7 @@
mScreenDecorations.start();
assertThat(mScreenDecorations.mIsRegistered, is(true));
- mScreenDecorations.onConfigurationChanged(new Configuration());
+ mScreenDecorations.onConfigChanged(new Configuration());
assertThat(mScreenDecorations.mIsRegistered, is(true));
}
@@ -1168,7 +1168,7 @@
assertThat(mScreenDecorations.mIsRegistered, is(true));
doReturn(false).when(mScreenDecorations).hasOverlays();
- mScreenDecorations.onConfigurationChanged(new Configuration());
+ mScreenDecorations.onConfigChanged(new Configuration());
assertThat(mScreenDecorations.mIsRegistered, is(false));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
new file mode 100644
index 0000000..202d9ce
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
@@ -0,0 +1,180 @@
+/*
+ * 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
+
+import android.os.Looper
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.dagger.GlobalRootComponent
+import com.android.systemui.dagger.SysUIComponent
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.flags.systemPropertiesHelper
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.process.processWrapper
+import com.android.systemui.startable.Dependencies
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
+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
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RunWithLooper
+class SystemUIApplicationTest : SysuiTestCase() {
+
+ private val app: SystemUIApplication = SystemUIApplication()
+ private lateinit var contextAvailableCallback:
+ SystemUIAppComponentFactoryBase.ContextAvailableCallback
+
+ @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ val kosmos = Kosmos()
+ @Mock private lateinit var initializer: SystemUIInitializer
+ @Mock private lateinit var rootComponent: GlobalRootComponent
+ @Mock private lateinit var sysuiComponent: SysUIComponent
+ @Mock private lateinit var bootCompleteCache: BootCompleteCacheImpl
+ @Mock private lateinit var initController: InitController
+
+ private val startableA = StartableA()
+ private val startableB = StartableB()
+ private val startableC = StartableC()
+ private val startableD = StartableD()
+ private val startableE = StartableE()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ app.attachBaseContext(context)
+ contextAvailableCallback =
+ SystemUIAppComponentFactoryBase.ContextAvailableCallback { initializer }
+ whenever(initializer.rootComponent).thenReturn(rootComponent)
+ whenever(initializer.sysUIComponent).thenReturn(sysuiComponent)
+ whenever(rootComponent.mainLooper).thenReturn(Looper.myLooper())
+ whenever(rootComponent.systemPropertiesHelper).thenReturn(kosmos.systemPropertiesHelper)
+ whenever(rootComponent.processWrapper).thenReturn(kosmos.processWrapper)
+ whenever(sysuiComponent.provideBootCacheImpl()).thenReturn(bootCompleteCache)
+ whenever(sysuiComponent.createDumpManager()).thenReturn(kosmos.dumpManager)
+ whenever(sysuiComponent.initController).thenReturn(initController)
+ kosmos.processWrapper.systemUser = true
+
+ app.setContextAvailableCallback(contextAvailableCallback)
+ }
+
+ @Test
+ fun testAppOnCreate() {
+ app.onCreate()
+ }
+
+ @Test
+ fun testStartServices_singleService() {
+ whenever(sysuiComponent.startables)
+ .thenReturn(mutableMapOf(StartableA::class.java to Provider { startableA }))
+ app.onCreate()
+ app.startServicesIfNeeded()
+ assertThat(startableA.started).isTrue()
+ }
+
+ @Test
+ fun testStartServices_twoServices() {
+ whenever(sysuiComponent.startables)
+ .thenReturn(
+ mutableMapOf(
+ StartableA::class.java to Provider { startableA },
+ StartableB::class.java to Provider { startableB }
+ )
+ )
+ app.onCreate()
+ app.startServicesIfNeeded()
+ assertThat(startableA.started).isTrue()
+ assertThat(startableB.started).isTrue()
+ }
+
+ @Test
+ fun testStartServices_simpleDependency() {
+ whenever(sysuiComponent.startables)
+ .thenReturn(
+ mutableMapOf(
+ StartableC::class.java to Provider { startableC },
+ StartableA::class.java to Provider { startableA },
+ StartableB::class.java to Provider { startableB }
+ )
+ )
+ app.onCreate()
+ app.startServicesIfNeeded()
+ assertThat(startableA.started).isTrue()
+ assertThat(startableB.started).isTrue()
+ assertThat(startableC.started).isTrue()
+ assertThat(startableC.order).isGreaterThan(startableA.order)
+ }
+
+ @Test
+ fun testStartServices_complexDependency() {
+ whenever(sysuiComponent.startables)
+ .thenReturn(
+ mutableMapOf(
+ StartableE::class.java to Provider { startableE },
+ StartableC::class.java to Provider { startableC },
+ StartableD::class.java to Provider { startableD },
+ StartableA::class.java to Provider { startableA },
+ StartableB::class.java to Provider { startableB }
+ )
+ )
+ app.onCreate()
+ app.startServicesIfNeeded()
+ assertThat(startableA.started).isTrue()
+ assertThat(startableB.started).isTrue()
+ assertThat(startableC.started).isTrue()
+ assertThat(startableD.started).isTrue()
+ assertThat(startableE.started).isTrue()
+ assertThat(startableC.order).isGreaterThan(startableA.order)
+ assertThat(startableD.order).isGreaterThan(startableA.order)
+ assertThat(startableD.order).isGreaterThan(startableB.order)
+ assertThat(startableE.order).isGreaterThan(startableB.order)
+ assertThat(startableE.order).isGreaterThan(startableD.order)
+ }
+
+ open class TestableStartable : CoreStartable {
+ companion object {
+ var startOrder = 0
+ }
+
+ var started = false
+ var order = -1
+
+ override fun start() {
+ started = true
+ order = startOrder
+ startOrder++
+ }
+ }
+
+ class StartableA : TestableStartable()
+ class StartableB : TestableStartable()
+
+ @Dependencies(StartableA::class) class StartableC : TestableStartable()
+
+ @Dependencies(StartableA::class, StartableB::class) class StartableD : TestableStartable()
+
+ @Dependencies(StartableD::class, StartableB::class) class StartableE : TestableStartable()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index f8856c9..bd49927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -102,8 +102,9 @@
getContext().getMainThreadHandler(), mCommandQueue,
mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger);
- mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
- mContext.getSystemService(DisplayManager.class));
+ mMagnification.mWindowMagnificationControllerSupplier =
+ new FakeWindowMagnificationControllerSupplier(
+ mContext.getSystemService(DisplayManager.class));
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
mContext.getSystemService(DisplayManager.class));
@@ -201,10 +202,10 @@
verify(mMagnificationSettingsController).setMagnificationScale(eq(testScale));
}
- private class FakeControllerSupplier extends
+ private class FakeWindowMagnificationControllerSupplier extends
DisplayIdIndexSupplier<WindowMagnificationController> {
- FakeControllerSupplier(DisplayManager displayManager) {
+ FakeWindowMagnificationControllerSupplier(DisplayManager displayManager) {
super(displayManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index d0e1678..3b5cbea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -124,7 +124,7 @@
getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
getContext().getSystemService(DisplayManager.class), mA11yLogger);
- mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
+ mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
mContext.getSystemService(DisplayManager.class), mMagnificationSettingsController);
@@ -325,9 +325,9 @@
@Test
public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() {
final WindowMagnificationController mController = mock(WindowMagnificationController.class);
- mMagnification.mMagnificationControllerSupplier = new FakeControllerSupplier(
+ mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mController);
- mMagnification.mMagnificationControllerSupplier.get(TEST_DISPLAY);
+ mMagnification.mWindowMagnificationControllerSupplier.get(TEST_DISPLAY);
mOverviewProxyListener.onConnectionChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
similarity index 84%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index fd258e3..9bcab57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -40,12 +40,12 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-/** Tests for {@link DismissAnimationController}. */
+/** Tests for {@link DragToInteractAnimationController}. */
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class DismissAnimationControllerTest extends SysuiTestCase {
- private DismissAnimationController mDismissAnimationController;
+public class DragToInteractAnimationControllerTest extends SysuiTestCase {
+ private DragToInteractAnimationController mDragToInteractAnimationController;
private DismissView mDismissView;
@Rule
@@ -65,19 +65,20 @@
stubMenuViewAppearance);
mDismissView = spy(new DismissView(mContext));
DismissViewUtils.setup(mDismissView);
- mDismissAnimationController = new DismissAnimationController(mDismissView, stubMenuView);
+ mDragToInteractAnimationController = new DragToInteractAnimationController(
+ mDismissView, stubMenuView);
}
@Test
public void showDismissView_success() {
- mDismissAnimationController.showDismissView(true);
+ mDragToInteractAnimationController.showDismissView(true);
verify(mDismissView).show();
}
@Test
public void hideDismissView_success() {
- mDismissAnimationController.showDismissView(false);
+ mDragToInteractAnimationController.showDismissView(false);
verify(mDismissView).hide();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 7f12c05..9c8de30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -62,7 +62,7 @@
@Mock
private SecureSettings mSecureSettings;
@Mock
- private DismissAnimationController.DismissCallback mStubDismissCallback;
+ private DragToInteractAnimationController.DismissCallback mStubDismissCallback;
private RecyclerView mStubListView;
private MenuView mMenuView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 9797f2a..e1522f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -68,7 +68,7 @@
private MenuView mStubMenuView;
private MenuListViewTouchHandler mTouchHandler;
private MenuAnimationController mMenuAnimationController;
- private DismissAnimationController mDismissAnimationController;
+ private DragToInteractAnimationController mDragToInteractAnimationController;
private RecyclerView mStubListView;
private DismissView mDismissView;
@@ -92,10 +92,10 @@
mStubMenuView, stubMenuViewAppearance));
mDismissView = spy(new DismissView(mContext));
DismissViewUtils.setup(mDismissView);
- mDismissAnimationController =
- spy(new DismissAnimationController(mDismissView, mStubMenuView));
+ mDragToInteractAnimationController =
+ spy(new DragToInteractAnimationController(mDismissView, mStubMenuView));
mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
- mDismissAnimationController);
+ mDragToInteractAnimationController);
final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(mStubTargets);
mStubListView = (RecyclerView) mStubMenuView.getChildAt(0);
mStubListView.setAdapter(stubAdapter);
@@ -115,7 +115,7 @@
@Test
public void onActionMoveEvent_notConsumedEvent_shouldMoveToPosition() {
- doReturn(false).when(mDismissAnimationController).maybeConsumeMoveMotionEvent(
+ doReturn(false).when(mDragToInteractAnimationController).maybeConsumeMoveMotionEvent(
any(MotionEvent.class));
final int offset = 100;
final MotionEvent stubDownEvent =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 4c510ee..1f7dd6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -29,7 +29,7 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
index e2aa984..647dae6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
@@ -20,10 +20,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import org.junit.Assert.assertFalse
import org.junit.Before
@@ -42,8 +41,7 @@
@Mock lateinit var udfpsBpView: UdfpsBpView
@Mock lateinit var statusBarStateController: StatusBarStateController
- @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
- @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ @Mock lateinit var shadeInteractor: ShadeInteractor
@Mock lateinit var systemUIDialogManager: SystemUIDialogManager
@Mock lateinit var dumpManager: DumpManager
@@ -55,7 +53,7 @@
UdfpsBpViewController(
udfpsBpView,
statusBarStateController,
- primaryBouncerInteractor,
+ shadeInteractor,
systemUIDialogManager,
dumpManager
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
index 27d93eb..8f0e910 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
@@ -24,7 +24,6 @@
import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
-import android.platform.test.annotations.RequiresFlagsEnabled
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.AuthenticationReason
@@ -48,7 +47,6 @@
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
-@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
@@ -62,6 +60,7 @@
@Before
fun setUp() {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
underTest = BiometricStatusRepositoryImpl(testScope.backgroundScope, biometricManager)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
index 6978923..d7b7d79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
@@ -19,7 +19,6 @@
import android.app.ActivityManager
import android.app.ActivityTaskManager
import android.content.ComponentName
-import android.platform.test.annotations.RequiresFlagsEnabled
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
@@ -44,7 +43,6 @@
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
-@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
@@ -59,6 +57,7 @@
@Before
fun setup() {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
biometricStatusRepository = FakeBiometricStatusRepository()
underTest = BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
index 67d3a20..640807b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
@@ -36,8 +36,8 @@
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index b4ae00d..cb26178 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -58,7 +58,6 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -67,6 +66,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@@ -217,6 +217,7 @@
deviceEntrySideFpsOverlayInteractor =
DeviceEntrySideFpsOverlayInteractor(
+ testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
primaryBouncerInteractor,
@@ -260,14 +261,14 @@
SideFpsOverlayViewBinder(
testScope.backgroundScope,
mContext,
- biometricStatusInteractor,
- displayStateInteractor,
- deviceEntrySideFpsOverlayInteractor,
- fpsUnlockTracker,
- layoutInflater,
- sideFpsProgressBarViewModel,
- sfpsSensorInteractor,
- windowManager
+ { biometricStatusInteractor },
+ { displayStateInteractor },
+ { deviceEntrySideFpsOverlayInteractor },
+ { fpsUnlockTracker },
+ { layoutInflater },
+ { sideFpsProgressBarViewModel },
+ { sfpsSensorInteractor },
+ { windowManager }
)
context.addMockSystemService(DisplayManager::class.java, displayManager)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
index fd5a584..1b6aaab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
@@ -22,17 +22,13 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
-import com.android.systemui.keyguard.ui.viewmodel.deviceEntryIconViewModelTransitionsMock
+import com.android.systemui.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.systemUIDialogManager
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -48,25 +44,14 @@
@SmallTest
@RunWith(JUnit4::class)
class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
- val kosmos =
+ private val kosmos =
testKosmos().apply {
fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
}
- val testScope = kosmos.testScope
-
- private val testDeviceEntryIconTransitionAlpha = MutableStateFlow(0f)
- private val testDeviceEntryIconTransition: DeviceEntryIconTransition
- get() =
- object : DeviceEntryIconTransition {
- override val deviceEntryParentViewAlpha: Flow<Float> =
- testDeviceEntryIconTransitionAlpha.asStateFlow()
- }
-
- init {
- kosmos.deviceEntryIconViewModelTransitionsMock.add(testDeviceEntryIconTransition)
- }
private val systemUIDialogManager = kosmos.systemUIDialogManager
private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
+ private val testScope = kosmos.testScope
+ private val deviceEntryIconViewModelTransition = kosmos.fakeDeviceEntryIconViewModelTransition
private val underTest = kosmos.deviceEntryUdfpsTouchOverlayViewModel
@Captor
@@ -82,7 +67,7 @@
testScope.runTest {
val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
- testDeviceEntryIconTransitionAlpha.value = 1f
+ deviceEntryIconViewModelTransition.setDeviceEntryParentViewAlpha(1f)
runCurrent()
verify(systemUIDialogManager).registerListener(sysuiDialogListenerCaptor.capture())
@@ -96,7 +81,7 @@
testScope.runTest {
val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
- testDeviceEntryIconTransitionAlpha.value = .3f
+ deviceEntryIconViewModelTransition.setDeviceEntryParentViewAlpha(.3f)
runCurrent()
verify(systemUIDialogManager).registerListener(sysuiDialogListenerCaptor.capture())
@@ -110,7 +95,7 @@
testScope.runTest {
val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
- testDeviceEntryIconTransitionAlpha.value = 1f
+ deviceEntryIconViewModelTransition.setDeviceEntryParentViewAlpha(1f)
runCurrent()
verify(systemUIDialogManager).registerListener(sysuiDialogListenerCaptor.capture())
@@ -124,7 +109,7 @@
testScope.runTest {
val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
- testDeviceEntryIconTransitionAlpha.value = 0f
+ deviceEntryIconViewModelTransition.setDeviceEntryParentViewAlpha(0f)
runCurrent()
bouncerRepository.setAlternateVisible(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 2267bdc..823b952 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -56,7 +56,6 @@
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -65,6 +64,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@@ -220,6 +220,7 @@
deviceEntrySideFpsOverlayInteractor =
DeviceEntrySideFpsOverlayInteractor(
+ testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
primaryBouncerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index aa0d7b6..45a426e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -25,6 +25,7 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FaceSensorInfo
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -35,8 +36,6 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
@@ -107,8 +106,7 @@
suspend fun TestScope.init() {
userRepository.setSelectedUserInfo(PRIMARY_USER)
- val featureFlags =
- FakeFeatureFlagsClassic().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) }
+ mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
primaryBouncerInteractor =
PrimaryBouncerInteractor(
bouncerRepository,
@@ -131,7 +129,6 @@
repository = repository,
userRepository = userRepository,
countDownTimerUtil = countDownTimerUtil,
- featureFlags = featureFlags,
updateMonitor = updateMonitor,
biometricSettingsRepository = biometricSettingsRepository,
applicationScope = this.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
index 16b2ed6..9c5cd71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
@@ -140,21 +140,76 @@
}
assertThat(widgets())
.containsExactly(
+ communalItemRankEntry2,
+ communalWidgetItemEntry2,
communalItemRankEntry1,
communalWidgetItemEntry1,
- communalItemRankEntry2,
+ )
+ .inOrder()
+
+ // swapped priorities
+ val widgetIdsToPriorityMap = mapOf(widgetInfo1.widgetId to 2, widgetInfo2.widgetId to 1)
+ communalWidgetDao.updateWidgetOrder(widgetIdsToPriorityMap)
+ assertThat(widgets())
+ .containsExactly(
+ communalItemRankEntry1.copy(rank = 2),
+ communalWidgetItemEntry1,
+ communalItemRankEntry2.copy(rank = 1),
communalWidgetItemEntry2
)
+ .inOrder()
+ }
- val widgetIdsInNewOrder = listOf(widgetInfo2.widgetId, widgetInfo1.widgetId)
- communalWidgetDao.updateWidgetOrder(widgetIdsInNewOrder)
+ @Test
+ fun addNewWidgetWithReorder_emitsWidgetsInNewOrder() =
+ testScope.runTest {
+ val existingWidgets = listOf(widgetInfo1, widgetInfo2)
+ val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+ existingWidgets.forEach {
+ val (widgetId, provider, priority) = it
+ communalWidgetDao.addWidget(
+ widgetId = widgetId,
+ provider = provider,
+ priority = priority,
+ )
+ }
assertThat(widgets())
.containsExactly(
communalItemRankEntry2,
communalWidgetItemEntry2,
communalItemRankEntry1,
- communalWidgetItemEntry1
+ communalWidgetItemEntry1,
)
+ .inOrder()
+
+ // map with no item in the middle at index 1
+ val widgetIdsToIndexMap = mapOf(widgetInfo1.widgetId to 1, widgetInfo2.widgetId to 3)
+ communalWidgetDao.updateWidgetOrder(widgetIdsToIndexMap)
+ assertThat(widgets())
+ .containsExactly(
+ communalItemRankEntry2.copy(rank = 3),
+ communalWidgetItemEntry2,
+ communalItemRankEntry1.copy(rank = 1),
+ communalWidgetItemEntry1,
+ )
+ .inOrder()
+ // add the new middle item that we left space for.
+ communalWidgetDao.addWidget(
+ widgetId = widgetInfo3.widgetId,
+ provider = widgetInfo3.provider,
+ priority = 2,
+ )
+ assertThat(widgets())
+ .containsExactly(
+ communalItemRankEntry2.copy(rank = 3),
+ communalWidgetItemEntry2,
+ communalItemRankEntry3.copy(rank = 2),
+ communalWidgetItemEntry3,
+ communalItemRankEntry1.copy(rank = 1),
+ communalWidgetItemEntry1,
+ )
+ .inOrder()
}
data class FakeWidgetMetadata(
@@ -176,8 +231,15 @@
provider = ComponentName("pk_name", "cls_name_2"),
priority = 2
)
+ val widgetInfo3 =
+ FakeWidgetMetadata(
+ widgetId = 3,
+ provider = ComponentName("pk_name", "cls_name_3"),
+ priority = 3
+ )
val communalItemRankEntry1 = CommunalItemRank(uid = 1L, rank = widgetInfo1.priority)
val communalItemRankEntry2 = CommunalItemRank(uid = 2L, rank = widgetInfo2.priority)
+ val communalItemRankEntry3 = CommunalItemRank(uid = 3L, rank = widgetInfo3.priority)
val communalWidgetItemEntry1 =
CommunalWidgetItem(
uid = 1L,
@@ -192,5 +254,12 @@
componentName = widgetInfo2.provider.flattenToString(),
itemId = communalItemRankEntry2.uid,
)
+ val communalWidgetItemEntry3 =
+ CommunalWidgetItem(
+ uid = 3L,
+ widgetId = widgetInfo3.widgetId,
+ componentName = widgetInfo3.provider.flattenToString(),
+ itemId = communalItemRankEntry3.uid,
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
new file mode 100644
index 0000000..e158e47
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.deviceentry.domain.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.fakeAccessibilityRepository
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.data.ui.viewmodel.deviceEntryUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+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.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsAccessibilityOverlayViewModelTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
+ }
+ private val deviceEntryIconTransition = kosmos.fakeDeviceEntryIconViewModelTransition
+ private val testScope = kosmos.testScope
+ private val accessibilityRepository = kosmos.fakeAccessibilityRepository
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
+ private val deviceEntryFingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
+ private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
+ private val shadeRepository = kosmos.fakeShadeRepository
+ private val underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
+
+ @Test
+ fun visible() =
+ testScope.runTest {
+ val visible by collectLastValue(underTest.visible)
+ setupVisibleStateOnLockscreen()
+ assertThat(visible).isTrue()
+ }
+
+ @Test
+ fun touchExplorationNotEnabled_overlayNotVisible() =
+ testScope.runTest {
+ val visible by collectLastValue(underTest.visible)
+ setupVisibleStateOnLockscreen()
+ accessibilityRepository.isTouchExplorationEnabled.value = false
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun deviceEntryFgIconViewModelAod_overlayNotVisible() =
+ testScope.runTest {
+ val visible by collectLastValue(underTest.visible)
+ setupVisibleStateOnLockscreen()
+
+ // AOD
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ )
+ )
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun deviceUnlocked_overlayNotVisible() =
+ testScope.runTest {
+ val visible by collectLastValue(underTest.visible)
+ setupVisibleStateOnLockscreen()
+ deviceEntryRepository.setUnlocked(true)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun deviceEntryViewAlpha0_overlayNotVisible() =
+ testScope.runTest {
+ val visible by collectLastValue(underTest.visible)
+ setupVisibleStateOnLockscreen()
+ deviceEntryIconTransition.setDeviceEntryParentViewAlpha(0f)
+ assertThat(visible).isFalse()
+ }
+
+ private suspend fun setupVisibleStateOnLockscreen() {
+ // A11y enabled
+ accessibilityRepository.isTouchExplorationEnabled.value = true
+
+ // Transition alpha is 1f
+ deviceEntryIconTransition.setDeviceEntryParentViewAlpha(1f)
+
+ // Listening for UDFPS
+ fingerprintPropertyRepository.supportsUdfps()
+ deviceEntryFingerprintAuthRepository.setIsRunning(true)
+ deviceEntryRepository.setUnlocked(false)
+
+ // Lockscreen
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ )
+ )
+
+ // Shade not expanded
+ shadeRepository.qsExpansion.value = 0f
+ shadeRepository.lockscreenShadeExpansion.value = 0f
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
index 71a56cd..c22d35c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
@@ -123,4 +123,25 @@
assertEquals(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5F), latest)
}
+
+ @Test
+ fun onArrowUp_afterStartTrackingTouch_ArrowUpProduced() = runTest {
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onStartTrackingTouch(seekBar)
+ eventProducer.onArrowUp()
+
+ assertEquals(SliderEvent(SliderEventType.ARROW_UP, 0f), latest)
+ }
+
+ @Test
+ fun onArrowUp_afterChangeByProgram_ArrowUpProduced_withProgress() = runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, false)
+ eventProducer.onArrowUp()
+
+ assertEquals(SliderEvent(SliderEventType.ARROW_UP, 0.5f), latest)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
index 8d12e49..db04962 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderTrackerTest.kt
@@ -528,6 +528,194 @@
verifyNoMoreInteractions(sliderStateListener)
}
+ @Test
+ fun onProgressChangeByProgram_atTheMiddle_onIdle_movesToArrowHandleMovedOnce() = runTest {
+ // GIVEN an initialized tracker in the IDLE state
+ initTracker(testScheduler)
+
+ // GIVEN a progress due to an external source that lands at the middle of the slider
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the state moves to ARROW_HANDLE_MOVED_ONCE and the listener is called to play
+ // haptics
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.ARROW_HANDLE_MOVED_ONCE)
+ verify(sliderStateListener).onSelectAndArrow(progress)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_atUpperBookend_onIdle_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the IDLE state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // GIVEN a progress due to an external source that lands at the upper bookend
+ val progress = config.upperBookendThreshold + 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes upper bookend haptics before moving back to IDLE
+ verify(sliderStateListener).onUpperBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_atLowerBookend_onIdle_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the IDLE state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+
+ // WHEN a progress is recorded due to an external source that lands at the lower bookend
+ val progress = config.lowerBookendThreshold - 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes lower bookend haptics before moving to IDLE
+ verify(sliderStateListener).onLowerBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
+ @Test
+ fun onArrowUp_onArrowMovedOnce_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+ // WHEN the external stimulus is released
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.ARROW_UP, progress))
+
+ // THEN the tracker moves back to IDLE and there are no haptics
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onStartTrackingTouch_onArrowMovedOnce_movesToWait() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+ // WHEN the slider starts tracking touch
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // THEN the tracker moves back to WAIT and starts the waiting job. Also, there are no
+ // haptics
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.WAIT)
+ assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_onArrowMovedOnce_movesToArrowMovesContinuously() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVED_ONCE state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVED_ONCE)
+
+ // WHEN the slider gets an external progress change
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker moves to ARROW_HANDLE_MOVES_CONTINUOUSLY and calls the appropriate
+ // haptics
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+ verify(sliderStateListener).onProgress(progress)
+ }
+
+ @Test
+ fun onArrowUp_onArrowMovesContinuously_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the external stimulus is released
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.ARROW_UP, progress))
+
+ // THEN the tracker moves to IDLE and no haptics are played
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onStartTrackingTouch_onArrowMovesContinuously_movesToWait() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider starts tracking touch
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, progress))
+
+ // THEN the tracker moves to WAIT and the wait job starts.
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.WAIT)
+ assertThat(mSeekableSliderTracker.isWaiting).isTrue()
+ verifyZeroInteractions(sliderStateListener)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_onArrowMovesContinuously_preservesState() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ initTracker(testScheduler)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider changes progress programmatically at the middle
+ val progress = 0.5f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker stays in the same state and haptics are delivered appropriately
+ assertThat(mSeekableSliderTracker.currentState)
+ .isEqualTo(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+ verify(sliderStateListener).onProgress(progress)
+ }
+
+ @Test
+ fun onProgramProgress_atLowerBookend_onArrowMovesContinuously_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider reaches the lower bookend programmatically
+ val progress = config.lowerBookendThreshold - 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes lower bookend haptics before moving to IDLE
+ verify(sliderStateListener).onLowerBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
+ @Test
+ fun onProgramProgress_atUpperBookend_onArrowMovesContinuously_movesToIdle() = runTest {
+ // GIVEN an initialized tracker in the ARROW_HANDLE_MOVES_CONTINUOUSLY state
+ val config = SeekableSliderTrackerConfig()
+ initTracker(testScheduler, config)
+ mSeekableSliderTracker.setState(SliderState.ARROW_HANDLE_MOVES_CONTINUOUSLY)
+
+ // WHEN the slider reaches the lower bookend programmatically
+ val progress = config.upperBookendThreshold + 0.01f
+ sliderEventProducer.sendEvent(
+ SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, progress)
+ )
+
+ // THEN the tracker executes upper bookend haptics before moving to IDLE
+ verify(sliderStateListener).onUpperBookend()
+ assertThat(mSeekableSliderTracker.currentState).isEqualTo(SliderState.IDLE)
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
private fun initTracker(
scheduler: TestCoroutineScheduler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 70d3f81..027dfa1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.domain.interactor
import android.os.Handler
-import android.platform.test.annotations.RequiresFlagsEnabled
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
@@ -58,7 +57,6 @@
import org.mockito.junit.MockitoRule
@OptIn(ExperimentalCoroutinesApi::class)
-@RequiresFlagsEnabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
@SmallTest
@RunWith(JUnit4::class)
class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
@@ -80,6 +78,7 @@
@Before
fun setup() {
+ mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
primaryBouncerInteractor =
PrimaryBouncerInteractor(
bouncerRepository,
@@ -110,6 +109,7 @@
)
underTest =
DeviceEntrySideFpsOverlayInteractor(
+ testScope.backgroundScope,
mContext,
FakeDeviceEntryFingerprintAuthRepository(),
primaryBouncerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 6eb95bd..769cf45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -42,7 +42,6 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
@@ -55,6 +54,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 3109e76..ad86ee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.util.mockito.whenever
@@ -70,7 +71,8 @@
@Mock private lateinit var communalTutorialIndicatorSection: CommunalTutorialIndicatorSection
@Mock private lateinit var clockSection: ClockSection
@Mock private lateinit var smartspaceSection: SmartspaceSection
-
+ @Mock
+ private lateinit var udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -90,6 +92,7 @@
communalTutorialIndicatorSection,
clockSection,
smartspaceSection,
+ udfpsAccessibilityOverlaySection,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index e89b61f..070a0cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -17,22 +17,26 @@
package com.android.systemui.keyguard.ui.view.layout.sections
+import android.content.pm.PackageManager
+import android.content.res.Resources
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.Utils
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@@ -41,18 +45,10 @@
class ClockSectionTest : SysuiTestCase() {
@Mock private lateinit var keyguardClockInteractor: KeyguardClockInteractor
@Mock private lateinit var keyguardClockViewModel: KeyguardClockViewModel
- @Mock private lateinit var smartspaceViewModel: KeyguardSmartspaceViewModel
@Mock private lateinit var splitShadeStateController: SplitShadeStateController
- private var featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
private lateinit var underTest: ClockSection
- // smartspaceViewModel.getDimen("date_weather_view_height")
- private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
-
- // smartspaceViewModel.getDimen("enhanced_smartspace_height")
- private val ENHANCED_SMART_SPACE_HEIGHT = 11
-
private val SMALL_CLOCK_TOP_SPLIT_SHADE =
context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
@@ -77,18 +73,43 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- whenever(smartspaceViewModel.getDimen("date_weather_view_height"))
- .thenReturn(SMART_SPACE_DATE_WEATHER_HEIGHT)
- whenever(smartspaceViewModel.getDimen("enhanced_smartspace_height"))
- .thenReturn(ENHANCED_SMART_SPACE_HEIGHT)
+ val remoteResources = mock<Resources>()
+ whenever(
+ remoteResources.getIdentifier(
+ anyString(),
+ eq("dimen"),
+ anyString(),
+ )
+ )
+ .then { invocation ->
+ val name = invocation.arguments[0] as String
+ val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
+ if (index == -1) {
+ error(
+ "No entry for a dimension named \"$name\", please add it to the list above."
+ )
+ }
+ index
+ }
+ whenever(
+ remoteResources.getDimensionPixelSize(
+ anyInt(),
+ )
+ )
+ .then { invocation ->
+ val id = invocation.arguments[0] as Int
+ DIMENSION_BY_IDENTIFIER_NAME[id].second
+ }
+ val packageManager = mock<PackageManager>()
+ whenever(packageManager.getResourcesForApplication(anyString())).thenReturn(remoteResources)
+ mContext.setMockPackageManager(packageManager)
+
underTest =
ClockSection(
keyguardClockInteractor,
keyguardClockViewModel,
- smartspaceViewModel,
mContext,
splitShadeStateController,
- featureFlags
)
}
@@ -147,24 +168,6 @@
assetSmallClockTop(cs, expectedSmallClockTopMargin)
}
- @Test
- fun testLargeClockShouldBeCentered() {
- underTest.setClockShouldBeCentered(true)
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
- val constraint = cs.getConstraint(R.id.lockscreen_clock_view_large)
- assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
- }
-
- @Test
- fun testLargeClockShouldNotBeCentered() {
- underTest.setClockShouldBeCentered(false)
- val cs = ConstraintSet()
- underTest.applyDefaultConstraints(cs)
- val constraint = cs.getConstraint(R.id.lockscreen_clock_view_large)
- assertThat(constraint.layout.endToEnd).isEqualTo(R.id.split_shade_guideline)
- }
-
private fun setLargeClock(useLargeClock: Boolean) {
whenever(keyguardClockViewModel.useLargeClock).thenReturn(useLargeClock)
}
@@ -185,4 +188,14 @@
assertThat(smallClockConstraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
assertThat(smallClockConstraint.layout.topMargin).isEqualTo(expectedSmallClockTopMargin)
}
+
+ companion object {
+ private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
+ private val ENHANCED_SMART_SPACE_HEIGHT = 11
+ private val DIMENSION_BY_IDENTIFIER_NAME =
+ listOf(
+ "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
+ "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index bff27f6..28da957 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -29,7 +29,9 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.StateFlow
@@ -50,9 +52,9 @@
@Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock private lateinit var hasCustomWeatherDataDisplay: StateFlow<Boolean>
- private val smartspaceView = View(mContext).also { it.id = View.generateViewId() }
- private val weatherView = View(mContext).also { it.id = View.generateViewId() }
- private val dateView = View(mContext).also { it.id = View.generateViewId() }
+ private val smartspaceView = View(mContext).also { it.id = sharedR.id.bc_smartspace_view }
+ private val weatherView = View(mContext).also { it.id = sharedR.id.weather_smartspace_view }
+ private val dateView = View(mContext).also { it.id = sharedR.id.date_smartspace_view }
private lateinit var constraintLayout: ConstraintLayout
private lateinit var constraintSet: ConstraintSet
@@ -69,12 +71,11 @@
keyguardUnlockAnimationController,
)
constraintLayout = ConstraintLayout(mContext)
- whenever(lockscreenSmartspaceController.buildAndConnectView(constraintLayout))
+ whenever(lockscreenSmartspaceController.buildAndConnectView(any()))
.thenReturn(smartspaceView)
- whenever(lockscreenSmartspaceController.buildAndConnectWeatherView(constraintLayout))
+ whenever(lockscreenSmartspaceController.buildAndConnectWeatherView(any()))
.thenReturn(weatherView)
- whenever(lockscreenSmartspaceController.buildAndConnectDateView(constraintLayout))
- .thenReturn(dateView)
+ whenever(lockscreenSmartspaceController.buildAndConnectDateView(any())).thenReturn(dateView)
whenever(keyguardClockViewModel.hasCustomWeatherDataDisplay)
.thenReturn(hasCustomWeatherDataDisplay)
constraintSet = ConstraintSet()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 459a74c..ee1be10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -24,7 +24,6 @@
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
import com.android.systemui.SysuiTestCase
-import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
deleted file mode 100644
index 6512290..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ /dev/null
@@ -1,139 +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.keyguard.ui.viewmodel
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-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
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@ExperimentalCoroutinesApi
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class UdfpsAodViewModelTest : SysuiTestCase() {
- private val defaultPadding = 12
- private lateinit var underTest: UdfpsAodViewModel
-
- private lateinit var testScope: TestScope
- private lateinit var configRepository: FakeConfigurationRepository
- private lateinit var bouncerRepository: KeyguardBouncerRepository
- private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var shadeRepository: FakeShadeRepository
- private lateinit var keyguardInteractor: KeyguardInteractor
-
- @Mock private lateinit var dialogManager: SystemUIDialogManager
- @Mock private lateinit var burnInHelper: BurnInHelperWrapper
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
- testScope = TestScope()
- shadeRepository = FakeShadeRepository()
- KeyguardInteractorFactory.create().also {
- keyguardInteractor = it.keyguardInteractor
- keyguardRepository = it.repository
- configRepository = it.configurationRepository
- bouncerRepository = it.bouncerRepository
- }
- val udfpsKeyguardInteractor =
- UdfpsKeyguardInteractor(
- configRepository,
- BurnInInteractor(
- context,
- burnInHelper,
- testScope.backgroundScope,
- configRepository,
- keyguardInteractor,
- ),
- keyguardInteractor,
- shadeRepository,
- dialogManager,
- )
-
- underTest =
- UdfpsAodViewModel(
- udfpsKeyguardInteractor,
- context,
- )
- }
-
- @Test
- fun alphaAndVisibleUpdates_onDozeAmountChanges() =
- testScope.runTest {
- val alpha by collectLastValue(underTest.alpha)
- val visible by collectLastValue(underTest.isVisible)
-
- keyguardRepository.setDozeAmount(0f)
- runCurrent()
- assertThat(alpha).isEqualTo(0f)
- assertThat(visible).isFalse()
-
- keyguardRepository.setDozeAmount(.65f)
- runCurrent()
- assertThat(alpha).isEqualTo(.65f)
- assertThat(visible).isTrue()
-
- keyguardRepository.setDozeAmount(.23f)
- runCurrent()
- assertThat(alpha).isEqualTo(.23f)
- assertThat(visible).isTrue()
-
- keyguardRepository.setDozeAmount(1f)
- runCurrent()
- assertThat(alpha).isEqualTo(1f)
- assertThat(visible).isTrue()
- }
-
- @Test
- fun paddingUpdates_onScaleForResolutionChanges() =
- testScope.runTest {
- val padding by collectLastValue(underTest.padding)
-
- configRepository.setScaleForResolution(1f)
- runCurrent()
- assertThat(padding).isEqualTo(defaultPadding)
-
- configRepository.setScaleForResolution(2f)
- runCurrent()
- assertThat(padding).isEqualTo(defaultPadding * 2)
-
- configRepository.setScaleForResolution(.5f)
- runCurrent()
- assertThat(padding).isEqualTo((defaultPadding * .5f).toInt())
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
deleted file mode 100644
index 95b2fe5..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ /dev/null
@@ -1,130 +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.keyguard.ui.viewmodel
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-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
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-/** Tests UdfpsFingerprintViewModel specific flows. */
-@ExperimentalCoroutinesApi
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class UdfpsFingerprintViewModelTest : SysuiTestCase() {
- private val defaultPadding = 12
- private lateinit var underTest: FingerprintViewModel
-
- private lateinit var testScope: TestScope
- private lateinit var configRepository: FakeConfigurationRepository
- private lateinit var bouncerRepository: KeyguardBouncerRepository
- private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var fakeCommandQueue: FakeCommandQueue
- private lateinit var transitionRepository: FakeKeyguardTransitionRepository
- private lateinit var shadeRepository: FakeShadeRepository
-
- @Mock private lateinit var burnInHelper: BurnInHelperWrapper
- @Mock private lateinit var dialogManager: SystemUIDialogManager
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
- testScope = TestScope()
- configRepository = FakeConfigurationRepository()
- keyguardRepository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
- fakeCommandQueue = FakeCommandQueue()
- bouncerRepository = FakeKeyguardBouncerRepository()
- transitionRepository = FakeKeyguardTransitionRepository()
- shadeRepository = FakeShadeRepository()
- val keyguardInteractor =
- KeyguardInteractorFactory.create(
- repository = keyguardRepository,
- )
- .keyguardInteractor
-
- val transitionInteractor =
- KeyguardTransitionInteractorFactory.create(
- scope = testScope.backgroundScope,
- repository = transitionRepository,
- keyguardInteractor = keyguardInteractor,
- )
- .keyguardTransitionInteractor
-
- underTest =
- FingerprintViewModel(
- context,
- transitionInteractor,
- UdfpsKeyguardInteractor(
- configRepository,
- BurnInInteractor(
- context,
- burnInHelper,
- testScope.backgroundScope,
- configRepository,
- keyguardInteractor,
- ),
- keyguardInteractor,
- shadeRepository,
- dialogManager,
- ),
- keyguardInteractor,
- )
- }
-
- @Test
- fun paddingUpdates_onScaleForResolutionChanges() =
- testScope.runTest {
- val padding by collectLastValue(underTest.padding)
-
- configRepository.setScaleForResolution(1f)
- runCurrent()
- assertThat(padding).isEqualTo(defaultPadding)
-
- configRepository.setScaleForResolution(2f)
- runCurrent()
- assertThat(padding).isEqualTo(defaultPadding * 2)
-
- configRepository.setScaleForResolution(.5f)
- runCurrent()
- assertThat(padding).isEqualTo((defaultPadding * .5).toInt())
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
deleted file mode 100644
index 848a94b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ /dev/null
@@ -1,749 +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.keyguard.ui.viewmodel
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.settingslib.Utils
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.mock
-import com.android.wm.shell.animation.Interpolators
-import com.google.common.collect.Range
-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
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-
-/** Tests UDFPS lockscreen view model transitions. */
-@ExperimentalCoroutinesApi
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class UdfpsLockscreenViewModelTest : SysuiTestCase() {
- private val lockscreenColorResId = android.R.attr.textColorPrimary
- private val alternateBouncerResId = com.android.internal.R.attr.materialColorOnPrimaryFixed
- private val lockscreenColor = Utils.getColorAttrDefaultColor(context, lockscreenColorResId)
- private val alternateBouncerColor =
- Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
-
- @Mock private lateinit var dialogManager: SystemUIDialogManager
-
- private lateinit var underTest: UdfpsLockscreenViewModel
- private lateinit var testScope: TestScope
- private lateinit var transitionRepository: FakeKeyguardTransitionRepository
- private lateinit var configRepository: FakeConfigurationRepository
- private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var keyguardInteractor: KeyguardInteractor
- private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
- private lateinit var shadeRepository: FakeShadeRepository
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- testScope = TestScope()
- transitionRepository = FakeKeyguardTransitionRepository()
- shadeRepository = FakeShadeRepository()
- KeyguardInteractorFactory.create().also {
- keyguardInteractor = it.keyguardInteractor
- keyguardRepository = it.repository
- configRepository = it.configurationRepository
- bouncerRepository = it.bouncerRepository
- }
-
- val transitionInteractor =
- KeyguardTransitionInteractorFactory.create(
- scope = testScope.backgroundScope,
- repository = transitionRepository,
- keyguardInteractor = keyguardInteractor,
- )
- .keyguardTransitionInteractor
-
- underTest =
- UdfpsLockscreenViewModel(
- context,
- lockscreenColorResId,
- alternateBouncerResId,
- transitionInteractor,
- UdfpsKeyguardInteractor(
- configRepository,
- BurnInInteractor(
- context,
- burnInHelperWrapper = mock(),
- testScope.backgroundScope,
- configRepository,
- keyguardInteractor,
- ),
- keyguardInteractor,
- shadeRepository,
- dialogManager,
- ),
- keyguardInteractor,
- )
- }
-
- @Test
- fun goneToAodTransition() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
-
- // TransitionState.STARTED: gone -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "goneToAodTransition",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isFalse()
-
- // TransitionState.RUNNING: gone -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "goneToAodTransition",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isFalse()
-
- // TransitionState.FINISHED: gone -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "goneToAodTransition",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isFalse()
- }
-
- @Test
- fun lockscreenToAod() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-
- // TransitionState.STARTED: lockscreen -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "lockscreenToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
-
- // TransitionState.RUNNING: lockscreen -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "lockscreenToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: lockscreen -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "lockscreenToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isFalse()
- }
-
- @Test
- fun lockscreenShadeLockedToAod() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
- keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
-
- // TransitionState.STARTED: lockscreen -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "lockscreenToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isFalse()
-
- // TransitionState.RUNNING: lockscreen -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "lockscreenToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isFalse()
-
- // TransitionState.FINISHED: lockscreen -> AOD
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "lockscreenToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isFalse()
- }
-
- @Test
- fun aodToLockscreen() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
-
- // TransitionState.STARTED: AOD -> lockscreen
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "aodToLockscreen",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isFalse()
-
- // TransitionState.RUNNING: AOD -> lockscreen
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "aodToLockscreen",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: AOD -> lockscreen
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "aodToLockscreen",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
- }
-
- @Test
- fun lockscreenToAlternateBouncer() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-
- // TransitionState.STARTED: lockscreen -> alternate bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.ALTERNATE_BOUNCER,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "lockscreenToAlternateBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.RUNNING: lockscreen -> alternate bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.ALTERNATE_BOUNCER,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "lockscreenToAlternateBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: lockscreen -> alternate bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.ALTERNATE_BOUNCER,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "lockscreenToAlternateBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
- }
-
- fun alternateBouncerToPrimaryBouncer() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
-
- // TransitionState.STARTED: alternate bouncer -> primary bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.PRIMARY_BOUNCER,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "alternateBouncerToPrimaryBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.RUNNING: alternate bouncer -> primary bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.PRIMARY_BOUNCER,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "alternateBouncerToPrimaryBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: alternate bouncer -> primary bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.PRIMARY_BOUNCER,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "alternateBouncerToPrimaryBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isFalse()
- }
-
- fun alternateBouncerToAod() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
-
- // TransitionState.STARTED: alternate bouncer -> aod
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.AOD,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "alternateBouncerToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.RUNNING: alternate bouncer -> aod
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.AOD,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "alternateBouncerToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: alternate bouncer -> aod
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "alternateBouncerToAod",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isFalse()
- }
-
- @Test
- fun lockscreenToOccluded() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-
- // TransitionState.STARTED: lockscreen -> occluded
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.OCCLUDED,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "lockscreenToOccluded",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
-
- // TransitionState.RUNNING: lockscreen -> occluded
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.OCCLUDED,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "lockscreenToOccluded",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: lockscreen -> occluded
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.OCCLUDED,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "lockscreenToOccluded",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isFalse()
- }
-
- @Test
- fun occludedToLockscreen() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
-
- // TransitionState.STARTED: occluded -> lockscreen
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "occludedToLockscreen",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
-
- // TransitionState.RUNNING: occluded -> lockscreen
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "occludedToLockscreen",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: occluded -> lockscreen
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "occludedToLockscreen",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(lockscreenColor)
- assertThat(visible).isTrue()
- }
-
- @Test
- fun qsProgressChange() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
- givenTransitionToLockscreenFinished()
-
- // qsExpansion = 0f
- shadeRepository.setQsExpansion(0f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(visible).isEqualTo(true)
-
- // qsExpansion = .25
- shadeRepository.setQsExpansion(.2f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(.6f)
- assertThat(visible).isEqualTo(true)
-
- // qsExpansion = .5
- shadeRepository.setQsExpansion(.5f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isEqualTo(false)
-
- // qsExpansion = 1
- shadeRepository.setQsExpansion(1f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isEqualTo(false)
- }
-
- @Test
- fun shadeExpansionChanged() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
- givenTransitionToLockscreenFinished()
-
- // shadeExpansion = 0f
- shadeRepository.setUdfpsTransitionToFullShadeProgress(0f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(visible).isEqualTo(true)
-
- // shadeExpansion = .2
- shadeRepository.setUdfpsTransitionToFullShadeProgress(.2f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(.8f)
- assertThat(visible).isEqualTo(true)
-
- // shadeExpansion = .5
- shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(.5f)
- assertThat(visible).isEqualTo(true)
-
- // shadeExpansion = 1
- shadeRepository.setUdfpsTransitionToFullShadeProgress(1f)
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(0f)
- assertThat(visible).isEqualTo(false)
- }
-
- @Test
- fun dialogHideAffordancesRequestChanged() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- givenTransitionToLockscreenFinished()
- runCurrent()
- val captor = argumentCaptor<SystemUIDialogManager.Listener>()
- Mockito.verify(dialogManager).registerListener(captor.capture())
-
- captor.value.shouldHideAffordances(true)
- assertThat(transition?.alpha).isEqualTo(0f)
-
- captor.value.shouldHideAffordances(false)
- assertThat(transition?.alpha).isEqualTo(1f)
- }
-
- @Test
- fun occludedToAlternateBouncer() =
- testScope.runTest {
- val transition by collectLastValue(underTest.transition)
- val visible by collectLastValue(underTest.visible)
-
- // TransitionState.STARTED: occluded -> alternate bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.ALTERNATE_BOUNCER,
- value = 0f,
- transitionState = TransitionState.STARTED,
- ownerName = "occludedToAlternateBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(0f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.RUNNING: occluded -> alternate bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.ALTERNATE_BOUNCER,
- value = .6f,
- transitionState = TransitionState.RUNNING,
- ownerName = "occludedToAlternateBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale)
- .isEqualTo(Interpolators.FAST_OUT_SLOW_IN.getInterpolation(.6f))
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
-
- // TransitionState.FINISHED: occluded -> alternate bouncer
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.ALTERNATE_BOUNCER,
- value = 1f,
- transitionState = TransitionState.FINISHED,
- ownerName = "occludedToAlternateBouncer",
- )
- )
- runCurrent()
- assertThat(transition?.alpha).isEqualTo(1f)
- assertThat(transition?.scale).isEqualTo(1f)
- assertThat(transition?.color).isEqualTo(alternateBouncerColor)
- assertThat(visible).isTrue()
- }
-
- private suspend fun givenTransitionToLockscreenFinished() {
- transitionRepository.sendTransitionSteps(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- testScope
- )
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
index 8532ffe..94b9fa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
@@ -55,6 +55,7 @@
private const val KEY_ALT = "TEST_KEY_2"
private const val USER_MAIN = 0
private const val USER_GUEST = 10
+private const val PRIVATE_PROFILE = 12
private const val PACKAGE = "PKG"
private val INSTANCE_ID = InstanceId.fakeInstanceId(123)!!
private const val APP_UID = 99
@@ -82,6 +83,7 @@
private lateinit var mediaDataFilter: MediaDataFilter
private lateinit var dataMain: MediaData
private lateinit var dataGuest: MediaData
+ private lateinit var dataPrivateProfile: MediaData
private val clock = FakeSystemClock()
@Before
@@ -115,6 +117,7 @@
appUid = APP_UID
)
dataGuest = dataMain.copy(userId = USER_GUEST)
+ dataPrivateProfile = dataMain.copy(userId = PRIVATE_PROFILE)
whenever(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
whenever(smartspaceData.isActive).thenReturn(true)
@@ -130,8 +133,19 @@
private fun setUser(id: Int) {
whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+ whenever(lockscreenUserManager.isProfileAvailable(anyInt())).thenReturn(false)
whenever(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
- mediaDataFilter.handleUserSwitched(id)
+ whenever(lockscreenUserManager.isProfileAvailable(eq(id))).thenReturn(true)
+ whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(true)
+ mediaDataFilter.handleUserSwitched()
+ }
+
+ private fun setPrivateProfileUnavailable() {
+ whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
+ whenever(lockscreenUserManager.isCurrentProfile(eq(USER_MAIN))).thenReturn(true)
+ whenever(lockscreenUserManager.isCurrentProfile(eq(PRIVATE_PROFILE))).thenReturn(true)
+ whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(false)
+ mediaDataFilter.handleProfileChanged()
}
@Test
@@ -206,6 +220,20 @@
}
@Test
+ fun testOnProfileChanged_profileUnavailable_loadControls() {
+ // GIVEN that we had some media for both profiles
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
+ mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataPrivateProfile)
+ reset(listener)
+
+ // and we change profile status
+ setPrivateProfileUnavailable()
+
+ // THEN we should add the private profile media
+ verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+ }
+
+ @Test
fun hasAnyMedia_noMediaSet_returnsFalse() {
assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index ce999cb..72847a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -71,13 +71,13 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -584,7 +584,7 @@
}
@Test
- public void addDeviceToPlayMedia_triggersFromLocalMediaManager() {
+ public void addDeviceToPlayMedia_callsLocalMediaManager() {
MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
@@ -592,16 +592,15 @@
mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
- LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
- testMediaOutputController.mLocalMediaManager = testLocalMediaManager;
+ LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
+ testMediaOutputController.mLocalMediaManager = mockLocalMediaManager;
testMediaOutputController.addDeviceToPlayMedia(mMediaDevice2);
-
- verify(testLocalMediaManager).addDeviceToPlayMedia(mMediaDevice2);
+ verify(mockLocalMediaManager).addDeviceToPlayMedia(mMediaDevice2);
}
@Test
- public void removeDeviceFromPlayMedia_triggersFromLocalMediaManager() {
+ public void removeDeviceFromPlayMedia_callsLocalMediaManager() {
MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
null,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
@@ -609,12 +608,11 @@
mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
- LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
- testMediaOutputController.mLocalMediaManager = testLocalMediaManager;
+ LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
+ testMediaOutputController.mLocalMediaManager = mockLocalMediaManager;
testMediaOutputController.removeDeviceFromPlayMedia(mMediaDevice2);
-
- verify(testLocalMediaManager).removeDeviceFromPlayMedia(mMediaDevice2);
+ verify(mockLocalMediaManager).removeDeviceFromPlayMedia(mMediaDevice2);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
index bcbf666..16c92ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
@@ -18,38 +18,27 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_PSS_TASK_SWITCHER
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.taskswitcher.ui.TaskSwitcherNotificationCoordinator
-import com.android.systemui.util.mockito.whenever
-import org.junit.Before
+import com.android.systemui.util.mockito.mock
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
class MediaProjectionTaskSwitcherCoreStartableTest : SysuiTestCase() {
- @Mock private lateinit var flags: FeatureFlags
- @Mock private lateinit var coordinator: TaskSwitcherNotificationCoordinator
+ private val coordinator = mock<TaskSwitcherNotificationCoordinator>()
- private lateinit var coreStartable: MediaProjectionTaskSwitcherCoreStartable
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- coreStartable = MediaProjectionTaskSwitcherCoreStartable(coordinator, flags)
- }
+ private val coreStartable =
+ MediaProjectionTaskSwitcherCoreStartable(notificationCoordinatorLazy = { coordinator })
@Test
fun start_flagEnabled_startsCoordinator() {
- whenever(flags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)).thenReturn(true)
+ mSetFlagsRule.enableFlags(FLAG_PSS_TASK_SWITCHER)
coreStartable.start()
@@ -58,7 +47,7 @@
@Test
fun start_flagDisabled_doesNotStartCoordinator() {
- whenever(flags.isEnabled(Flags.PARTIAL_SCREEN_SHARING_TASK_SWITCHER)).thenReturn(false)
+ mSetFlagsRule.disableFlags(FLAG_PSS_TASK_SWITCHER)
coreStartable.start()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 4d42324..b7618d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -75,7 +75,6 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
@@ -463,7 +462,6 @@
// region setNoteTaskShortcutEnabled
@Test
- @Ignore("b/316332684")
fun setNoteTaskShortcutEnabled_setTrue() {
createNoteTaskController().setNoteTaskShortcutEnabled(value = true, userTracker.userHandle)
@@ -480,7 +478,6 @@
}
@Test
- @Ignore("b/316332684")
fun setNoteTaskShortcutEnabled_setFalse() {
createNoteTaskController().setNoteTaskShortcutEnabled(value = false, userTracker.userHandle)
@@ -497,7 +494,6 @@
}
@Test
- @Ignore("b/316332684")
fun setNoteTaskShortcutEnabled_workProfileUser_setTrue() {
whenever(context.createContextAsUser(eq(workUserInfo.userHandle), any()))
.thenReturn(workProfileContext)
@@ -519,7 +515,6 @@
}
@Test
- @Ignore("b/316332684")
fun setNoteTaskShortcutEnabled_workProfileUser_setFalse() {
whenever(context.createContextAsUser(eq(workUserInfo.userHandle), any()))
.thenReturn(workProfileContext)
@@ -738,7 +733,6 @@
// region internalUpdateNoteTaskAsUser
@Test
- @Ignore("b/316332684")
fun updateNoteTaskAsUserInternal_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
createNoteTaskController(isEnabled = true)
.launchUpdateNoteTaskAsUser(userTracker.userHandle)
@@ -772,7 +766,6 @@
}
@Test
- @Ignore("b/316332684")
fun updateNoteTaskAsUserInternal_noNotesRole_shouldDisableShortcuts() {
whenever(roleManager.getRoleHoldersAsUser(ROLE_NOTES, userTracker.userHandle))
.thenReturn(emptyList())
@@ -796,7 +789,6 @@
}
@Test
- @Ignore("b/316332684")
fun updateNoteTaskAsUserInternal_flagDisabled_shouldDisableShortcuts() {
createNoteTaskController(isEnabled = false)
.launchUpdateNoteTaskAsUser(userTracker.userHandle)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 5e2423a..ef7798e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -447,10 +447,6 @@
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
-
- // Remove spurious tiles, like dbg:mem
- specs.removeIf(spec -> !"spec1".equals(spec));
- assertEquals(1, specs.size());
}
@Test
@@ -458,10 +454,6 @@
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1");
List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
-
- // Remove spurious tiles, like dbg:mem
- specs.removeIf(spec -> !"spec1".equals(spec));
- assertEquals(1, specs.size());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 067218a..5201e5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -50,7 +50,6 @@
import com.android.systemui.qs.tiles.ScreenRecordTile
import com.android.systemui.qs.tiles.UiModeNightTile
import com.android.systemui.qs.tiles.WorkModeTile
-import com.android.systemui.util.leak.GarbageMonitor
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -117,7 +116,6 @@
@Mock private lateinit var dataSaverTile: DataSaverTile
@Mock private lateinit var nightDisplayTile: NightDisplayTile
@Mock private lateinit var nfcTile: NfcTile
- @Mock private lateinit var memoryTile: GarbageMonitor.MemoryTile
@Mock private lateinit var darkModeTile: UiModeNightTile
@Mock private lateinit var screenRecordTile: ScreenRecordTile
@Mock private lateinit var reduceBrightColorsTile: ReduceBrightColorsTile
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 9b61447..c7479fd5 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
@@ -30,8 +30,8 @@
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.res.R
-import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -65,9 +65,9 @@
@Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var dialogLauncherAnimator: DialogLaunchAnimator
- @Mock private lateinit var dialogFactory: SystemUIDialog.Factory
+ @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory
+ @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate
@Mock private lateinit var dialog: SystemUIDialog
- @Mock private lateinit var userContextProvider: UserContextProvider
private lateinit var testableLooper: TestableLooper
private lateinit var tile: RecordIssueTile
@@ -76,7 +76,8 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(host.context).thenReturn(mContext)
- whenever(dialogFactory.create(any())).thenReturn(dialog)
+ whenever(delegateFactory.create(any())).thenReturn(dialogDelegate)
+ whenever(dialogDelegate.createDialog()).thenReturn(dialog)
testableLooper = TestableLooper.get(this)
tile =
@@ -93,8 +94,7 @@
keyguardDismissUtil,
keyguardStateController,
dialogLauncherAnimator,
- dialogFactory,
- userContextProvider,
+ delegateFactory,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index c108a80..273ce85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -28,8 +28,8 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -82,7 +82,7 @@
TextView deviceClosedTitleTextView = controller.mRearDisplayEducationDialog.findViewById(
R.id.rear_display_title_text_view);
- controller.onConfigurationChanged(new Configuration());
+ controller.onConfigChanged(new Configuration());
assertTrue(controller.mRearDisplayEducationDialog.isShowing());
TextView deviceClosedTitleTextView2 = controller.mRearDisplayEducationDialog.findViewById(
R.id.rear_display_title_text_view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index c5d3524..7ce51ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -17,6 +17,9 @@
package com.android.systemui.recordissue
import android.app.Dialog
+import android.content.Context
+import android.content.SharedPreferences
+import android.os.UserHandle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.widget.Button
@@ -25,48 +28,107 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.SessionCreationSource
+import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
import com.android.systemui.model.SysUiState
+import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class RecordIssueDialogDelegateTest : SysuiTestCase() {
+ @Mock private lateinit var flags: FeatureFlagsClassic
+ @Mock private lateinit var devicePolicyResolver: ScreenCaptureDevicePolicyResolver
+ @Mock private lateinit var dprLazy: dagger.Lazy<ScreenCaptureDevicePolicyResolver>
+ @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
+ @Mock private lateinit var userContextProvider: UserContextProvider
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var userFileManager: UserFileManager
+ @Mock private lateinit var sharedPreferences: SharedPreferences
+
+ @Mock private lateinit var sysuiState: SysUiState
+ @Mock private lateinit var systemUIDialogManager: SystemUIDialogManager
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var bgExecutor: Executor
+ @Mock private lateinit var mainExecutor: Executor
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
private lateinit var dialog: SystemUIDialog
+ private lateinit var factory: SystemUIDialog.Factory
private lateinit var latch: CountDownLatch
@Before
fun setup() {
- val dialogFactory =
- SystemUIDialog.Factory(
- context,
- mock<FeatureFlags>(),
- mock<SystemUIDialogManager>(),
- mock<SysUiState>().apply {
- whenever(setFlag(anyInt(), anyBoolean())).thenReturn(this)
- },
- mock<BroadcastDispatcher>(),
- mock<DialogLaunchAnimator>()
+ MockitoAnnotations.initMocks(this)
+ whenever(dprLazy.get()).thenReturn(devicePolicyResolver)
+ whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+ whenever(userContextProvider.userContext).thenReturn(mContext)
+ whenever(
+ userFileManager.getSharedPreferences(
+ eq(RecordIssueTile.TILE_SPEC),
+ eq(Context.MODE_PRIVATE),
+ anyInt()
+ )
+ )
+ .thenReturn(sharedPreferences)
+
+ factory =
+ spy(
+ SystemUIDialog.Factory(
+ context,
+ flags,
+ systemUIDialogManager,
+ sysuiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator
+ )
)
latch = CountDownLatch(1)
dialog =
- RecordIssueDialogDelegate(dialogFactory, mock()) { latch.countDown() }.createDialog()
+ RecordIssueDialogDelegate(
+ factory,
+ userContextProvider,
+ userTracker,
+ flags,
+ bgExecutor,
+ mainExecutor,
+ dprLazy,
+ mediaProjectionMetricsLogger,
+ userFileManager,
+ ) {
+ latch.countDown()
+ }
+ .createDialog()
dialog.show()
}
@@ -91,4 +153,82 @@
dialog.getButton(Dialog.BUTTON_POSITIVE).callOnClick()
latch.await(1L, TimeUnit.MILLISECONDS)
}
+
+ @Test
+ fun screenCaptureDisabledDialog_isShown_whenFunctionalityIsDisabled() {
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+ .thenReturn(true)
+ whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+ .thenReturn(true)
+
+ val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+ screenRecordSwitch.isChecked = true
+
+ val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(bgExecutor).execute(bgCaptor.capture())
+ bgCaptor.value.run()
+
+ val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(mainExecutor).execute(mainCaptor.capture())
+ mainCaptor.value.run()
+
+ verify(mediaProjectionMetricsLogger, never())
+ .notifyProjectionInitiated(
+ anyInt(),
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ )
+ assertThat(screenRecordSwitch.isChecked).isFalse()
+ }
+
+ @Test
+ fun screenCapturePermissionDialog_isShown_correctly() {
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+ .thenReturn(false)
+ whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+ .thenReturn(false)
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
+ whenever(sharedPreferences.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false))
+ .thenReturn(false)
+
+ val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+ screenRecordSwitch.isChecked = true
+
+ val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(bgExecutor).execute(bgCaptor.capture())
+ bgCaptor.value.run()
+
+ val mainCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(mainExecutor).execute(mainCaptor.capture())
+ mainCaptor.value.run()
+
+ verify(mediaProjectionMetricsLogger)
+ .notifyProjectionInitiated(
+ anyInt(),
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ )
+ verify(factory).create(any<ScreenCapturePermissionDialogDelegate>())
+ }
+
+ @Test
+ fun noDialogsAreShown_forScreenRecord_whenApprovalIsAlreadyGiven() {
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES))
+ .thenReturn(false)
+ whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
+ .thenReturn(false)
+ whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(false)
+
+ val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
+ screenRecordSwitch.isChecked = true
+
+ val bgCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(bgExecutor).execute(bgCaptor.capture())
+ bgCaptor.value.run()
+
+ verify(mediaProjectionMetricsLogger)
+ .notifyProjectionInitiated(
+ anyInt(),
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ )
+ verify(factory, never()).create(any<ScreenCapturePermissionDialogDelegate>())
+ }
}
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 d4e8d37..72fc65b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -10,8 +10,6 @@
import androidx.constraintlayout.widget.Guideline
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
@@ -39,7 +37,6 @@
lateinit var detectionNoticeView: ViewGroup
lateinit var container: FrameLayout
- var featureFlags = FakeFeatureFlags()
lateinit var screenshotView: ViewGroup
val userHandle = UserHandle.of(5)
@@ -55,7 +52,6 @@
MessageContainerController(
workProfileMessageController,
screenshotDetectionController,
- featureFlags
)
screenshotView = ConstraintLayout(mContext)
workProfileData = WorkProfileMessageController.WorkProfileFirstRunData(appName, icon)
@@ -105,8 +101,6 @@
@Test
fun testOnScreenshotTakenScreenshotData_nothingToShow() {
- featureFlags.set(Flags.SCREENSHOT_DETECTION, true)
-
messageContainer.onScreenshotTaken(screenshotData)
verify(workProfileMessageController, never()).populateView(any(), any(), 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 c32d259..032ec74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -177,7 +177,7 @@
verify(context)
.registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
- assertThat(countActions()).isEqualTo(7)
+ 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()
@@ -185,6 +185,10 @@
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()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 88c728f..b94e483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -36,8 +36,12 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import dagger.Lazy
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.flow.timeout
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -59,7 +63,6 @@
@Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory
@Mock private lateinit var brightnessController: BrightnessController
@Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
- @Mock private lateinit var shadeInteractorLazy: Lazy<ShadeInteractor>
@Mock private lateinit var shadeInteractor: ShadeInteractor
private val clock = FakeSystemClock()
@@ -89,7 +92,6 @@
.thenReturn(brightnessSliderController)
`when`(brightnessSliderController.rootView).thenReturn(View(context))
`when`(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
- whenever(shadeInteractorLazy.get()).thenReturn(shadeInteractor)
whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
}
@@ -180,6 +182,22 @@
assertThat(activityRule.activity.isFinishing()).isFalse()
}
+ @OptIn(FlowPreview::class)
+ @Test
+ fun testFinishOnQSExpanded() = runTest {
+ val isQSExpanded = MutableStateFlow(false)
+ `when`(shadeInteractor.isQsExpanded).thenReturn(isQSExpanded)
+ activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
+
+ assertThat(activityRule.activity.isFinishing()).isFalse()
+
+ isQSExpanded.value = true
+ // Observe the activity's state until is it finishing or the timeout is reached, whatever
+ // comes first. This fixes the flakiness seen when using advanceUntilIdle().
+ activityRule.activity.finishing.timeout(100.milliseconds).takeWhile { !it }.collect {}
+ assertThat(activityRule.activity.isFinishing()).isTrue()
+ }
+
class TestDialog(
brightnessSliderControllerFactory: BrightnessSliderController.Factory,
brightnessControllerFactory: BrightnessController.Factory,
@@ -194,14 +212,14 @@
accessibilityMgr,
shadeInteractor
) {
- private var finishing = false
+ var finishing = MutableStateFlow(false)
override fun isFinishing(): Boolean {
- return finishing
+ return finishing.value
}
override fun requestFinish() {
- finishing = true
+ finishing.value = true
}
}
}
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 e572dcc..48baeb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -19,12 +19,10 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.google.common.truth.Truth.assertThat;
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -39,6 +37,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import android.annotation.IdRes;
import android.content.ContentResolver;
import android.content.res.Configuration;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 86d8d54..c21addc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -48,10 +48,9 @@
import com.android.systemui.compose.ComposeFacade.isComposeAvailable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON
import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_FEATURES
@@ -67,12 +66,9 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
-import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
@@ -81,7 +77,6 @@
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -101,7 +96,6 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -116,8 +110,9 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -197,9 +192,9 @@
featureFlagsClassic.set(TRACKPAD_GESTURE_COMMON, true)
featureFlagsClassic.set(TRACKPAD_GESTURE_FEATURES, false)
featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
- featureFlagsClassic.set(REVAMPED_BOUNCER_MESSAGES, true)
featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
testScope = TestScope()
fakeClock = FakeSystemClock()
@@ -242,7 +237,6 @@
repository = BouncerMessageRepositoryImpl(),
userRepository = FakeUserRepository(),
countDownTimerUtil = mock(CountDownTimerUtil::class.java),
- featureFlags = featureFlagsClassic,
updateMonitor = mock(KeyguardUpdateMonitor::class.java),
biometricSettingsRepository = FakeBiometricSettingsRepository(),
applicationScope = testScope.backgroundScope,
@@ -278,11 +272,7 @@
alternateBouncerInteractor,
mSelectedUserInteractor,
{ mock (JavaAdapter::class.java )},
- { mock(AlternateBouncerViewModel::class.java) },
- { mock(FalsingManager::class.java) },
- { mock(SwipeUpAnywhereGestureHandler::class.java) },
- { mock(TapGestureDetector::class.java) },
- { mock(AlternateBouncerUdfpsIconViewModel::class.java) },
+ { mock(AlternateBouncerDependencies::class.java) },
)
underTest.setupExpandedStatusBar()
underTest.setDragDownHelper(dragDownHelper)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index d9ff892..33d60ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
@@ -43,7 +44,6 @@
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.flags.SystemPropertiesHelper
@@ -55,11 +55,10 @@
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
@@ -69,7 +68,6 @@
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -189,9 +187,9 @@
featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
- featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
testScope = TestScope()
controller =
NotificationShadeWindowViewController(
@@ -232,7 +230,6 @@
repository = BouncerMessageRepositoryImpl(),
userRepository = FakeUserRepository(),
countDownTimerUtil = Mockito.mock(CountDownTimerUtil::class.java),
- featureFlags = featureFlags,
updateMonitor = Mockito.mock(KeyguardUpdateMonitor::class.java),
biometricSettingsRepository = FakeBiometricSettingsRepository(),
applicationScope = testScope.backgroundScope,
@@ -268,11 +265,7 @@
alternateBouncerInteractor,
mSelectedUserInteractor,
{ Mockito.mock(JavaAdapter::class.java) },
- { Mockito.mock(AlternateBouncerViewModel::class.java) },
- { Mockito.mock(FalsingManager::class.java) },
- { Mockito.mock(SwipeUpAnywhereGestureHandler::class.java) },
- { Mockito.mock(TapGestureDetector::class.java) },
- { Mockito.mock(AlternateBouncerUdfpsIconViewModel::class.java) },
+ { Mockito.mock(AlternateBouncerDependencies::class.java) },
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ee94cbb..ee27c5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.flags.Flags.TRANSIT_CLOCK
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockProviderPlugin
import com.android.systemui.plugins.clocks.ClockSettings
@@ -128,6 +129,7 @@
override fun createClock(settings: ClockSettings): ClockController =
createCallbacks[settings.clockId!!]!!(settings.clockId!!)
override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!(id)
+ override fun initialize(buffers: ClockMessageBuffers?) { }
fun addClock(
id: ClockId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index fef262f..e0e8d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -26,6 +26,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.customization.R
+import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR
import com.android.systemui.util.mockito.any
@@ -49,6 +50,9 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+private fun DefaultClockProvider.createClock(id: ClockId): DefaultClockController =
+ createClock(ClockSettings(id, null)) as DefaultClockController
+
@RunWith(AndroidTestingRunner::class)
@SmallTest
class DefaultClockProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
new file mode 100644
index 0000000..50349be
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.shared.notifications.data.repository
+
+import android.provider.Settings
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.shared.settings.data.repository.FakeSecureSettingsRepository
+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.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class NotificationSettingsRepositoryTest : SysuiTestCase() {
+
+ private lateinit var underTest: NotificationSettingsRepository
+
+ private lateinit var testScope: TestScope
+ private lateinit var secureSettingsRepository: FakeSecureSettingsRepository
+
+ @Before
+ fun setUp() {
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+ secureSettingsRepository = FakeSecureSettingsRepository()
+
+ underTest =
+ NotificationSettingsRepository(
+ scope = testScope.backgroundScope,
+ backgroundDispatcher = testDispatcher,
+ secureSettingsRepository = secureSettingsRepository,
+ )
+ }
+
+ @Test
+ fun testGetIsShowNotificationsOnLockscreenEnabled() =
+ testScope.runTest {
+ val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled)
+
+ secureSettingsRepository.setInt(
+ name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ value = 1,
+ )
+ assertThat(showNotifs).isEqualTo(true)
+
+ secureSettingsRepository.setInt(
+ name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ value = 0,
+ )
+ assertThat(showNotifs).isEqualTo(false)
+ }
+
+ @Test
+ fun testSetIsShowNotificationsOnLockscreenEnabled() =
+ testScope.runTest {
+ val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled)
+
+ underTest.setShowNotificationsOnLockscreenEnabled(true)
+ assertThat(showNotifs).isEqualTo(true)
+
+ underTest.setShowNotificationsOnLockscreenEnabled(false)
+ assertThat(showNotifs).isEqualTo(false)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 6eabf44..bc50c25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -17,13 +17,17 @@
package com.android.systemui.shared.plugins;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
@@ -40,7 +44,11 @@
import java.lang.ref.WeakReference;
import java.util.Collections;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -104,6 +112,7 @@
mPluginInstance = mPluginInstanceFactory.create(
mContext, mAppInfo, TEST_PLUGIN_COMPONENT_NAME,
TestPlugin.class, mPluginListener);
+ mPluginInstance.setIsDebug(true);
mPluginContext = new WeakReference<>(mPluginInstance.getPluginContext());
}
@@ -158,7 +167,7 @@
@Test
public void testOnAttach_SkipLoad() {
- mPluginListener.mAttachReturn = false;
+ mPluginListener.mOnAttach = () -> false;
mPluginInstance.onCreate();
assertEquals(1, mPluginListener.mAttachedCount);
assertEquals(0, mPluginListener.mLoadCount);
@@ -166,6 +175,65 @@
assertInstances(0, 0);
}
+ @Test
+ public void testLoadUnloadSimultaneous_HoldsUnload() throws Exception {
+ final Semaphore loadLock = new Semaphore(1);
+ final Semaphore unloadLock = new Semaphore(1);
+
+ mPluginListener.mOnAttach = () -> false;
+ mPluginListener.mOnLoad = () -> {
+ assertNotNull(mPluginInstance.getPlugin());
+
+ // Allow the bg thread the opportunity to delete the plugin
+ loadLock.release();
+ Thread.yield();
+ boolean isLocked = getLock(unloadLock, 1000);
+
+ // Ensure the bg thread failed to do delete the plugin
+ assertNotNull(mPluginInstance.getPlugin());
+ // We expect that bgThread deadlocked holding the semaphore
+ assertFalse(isLocked);
+ };
+
+ AtomicBoolean isBgThreadFailed = new AtomicBoolean(false);
+ Thread bgThread = new Thread(() -> {
+ assertTrue(getLock(unloadLock, 10));
+ assertTrue(getLock(loadLock, 4000)); // Wait for the foreground thread
+ assertNotNull(mPluginInstance.getPlugin());
+ // Attempt to delete the plugin, this should block until the load completes
+ mPluginInstance.unloadPlugin();
+ assertNull(mPluginInstance.getPlugin());
+ unloadLock.release();
+ loadLock.release();
+ });
+
+ // This protects the test suite from crashing due to the uncaught exception.
+ bgThread.setUncaughtExceptionHandler((Thread t, Throwable ex) -> {
+ Log.e("testLoadUnloadSimultaneous_HoldsUnload", "Exception from BG Thread", ex);
+ isBgThreadFailed.set(true);
+ });
+
+ loadLock.acquire();
+ mPluginInstance.onCreate();
+
+ assertNull(mPluginInstance.getPlugin());
+ bgThread.start();
+ mPluginInstance.loadPlugin();
+
+ bgThread.join(5000);
+ assertFalse(isBgThreadFailed.get());
+ assertNull(mPluginInstance.getPlugin());
+ }
+
+ private boolean getLock(Semaphore lock, long millis) {
+ try {
+ return lock.tryAcquire(millis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ex) {
+ fail();
+ return false;
+ }
+ }
+
// This target class doesn't matter, it just needs to have a Requires to hit the flow where
// the mock version info is called.
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
@@ -226,7 +294,10 @@
}
public class FakeListener implements PluginListener<TestPlugin> {
- public boolean mAttachReturn = true;
+ public Supplier<Boolean> mOnAttach = null;
+ public Runnable mOnDetach = null;
+ public Runnable mOnLoad = null;
+ public Runnable mOnUnload = null;
public int mAttachedCount = 0;
public int mDetachedCount = 0;
public int mLoadCount = 0;
@@ -236,13 +307,16 @@
public boolean onPluginAttached(PluginLifecycleManager<TestPlugin> manager) {
mAttachedCount++;
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
- return mAttachReturn;
+ return mOnAttach != null ? mOnAttach.get() : true;
}
@Override
public void onPluginDetached(PluginLifecycleManager<TestPlugin> manager) {
mDetachedCount++;
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ if (mOnDetach != null) {
+ mOnDetach.run();
+ }
}
@Override
@@ -261,6 +335,9 @@
assertEquals(expectedContext, pluginContext);
}
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ if (mOnLoad != null) {
+ mOnLoad.run();
+ }
}
@Override
@@ -274,6 +351,9 @@
assertEquals(expectedPlugin, plugin);
}
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ if (mOnUnload != null) {
+ mOnUnload.run();
+ }
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 76c4015..e1d9282 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.google.common.truth.Truth.assertThat;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 2bee7b8..d3febf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository;
import com.android.systemui.statusbar.domain.interactor.SilentNotificationStatusIconsVisibilityInteractor;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -151,6 +152,7 @@
@Test
public void testOnConnectReadStatusBarSetting() {
+ mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME);
NotificationListener.NotificationSettingsListener settingsListener =
mock(NotificationListener.NotificationSettingsListener.class);
mListener.addNotificationSettingsListener(settingsListener);
@@ -164,6 +166,7 @@
@Test
public void testOnStatusBarIconsBehaviorChanged() {
+ mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME);
NotificationListener.NotificationSettingsListener settingsListener =
mock(NotificationListener.NotificationSettingsListener.class);
mListener.addNotificationSettingsListener(settingsListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 42c7375..0c6f456 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -19,9 +19,12 @@
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.os.UserHandle.USER_ALL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
@@ -111,7 +114,9 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf(FLAG_ALLOW_PRIVATE_PROFILE);
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_ALLOW_PRIVATE_PROFILE,
+ FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS);
}
public NotificationLockscreenUserManagerTest(FlagsParameterization flags) {
@@ -245,6 +250,19 @@
}
@Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testInit() {
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+ mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
+ mLockscreenUserManager.setUpWithPresenter(mPresenter);
+
+ mBackgroundExecutor.runAllReady();
+
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
public void testGetCurrentProfiles() {
final SparseArray<UserInfo> expectedCurProfiles = new SparseArray<>();
expectedCurProfiles.put(mCurrentUser.id, mCurrentUser);
@@ -579,6 +597,29 @@
}
@Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testKeyguardManager_noPrivateNotifications() {
+ Mockito.clearInvocations(mDevicePolicyManager);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
+
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ // it's a global field, confirm secondary too
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
+ mSecondaryUser.id));
+ }
+
+ @Test
public void testDevicePolicy_noPrivateNotifications() {
Mockito.clearInvocations(mDevicePolicyManager);
// User allows notifications
@@ -699,6 +740,29 @@
}
@Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications_show() {
+ // KeyguardManager does not allow notifications
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ // DevicePolicy allows notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(0);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mKeyguardReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mKeyguardReceiver.onReceive(mContext,
+ new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false));
+
+ assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications() {
// KeyguardManager does not allow notifications
when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
@@ -718,6 +782,7 @@
}
@Test
+ @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
// DevicePolicy allows notifications
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 560ebc6..d3850be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static junit.framework.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index 40edea2..438b33d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -22,7 +22,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS
import com.android.systemui.statusbar.StatusBarState
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
index a56fb2c..7d8cf36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
@@ -1,11 +1,10 @@
package com.android.systemui.statusbar.notification
+import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import org.junit.Assert.assertEquals
@@ -20,8 +19,7 @@
@RunWith(JUnit4::class)
class RoundableTest : SysuiTestCase() {
private val targetView: View = mock()
- private val featureFlags = FakeFeatureFlags()
- private val roundable = FakeRoundable(targetView = targetView, featureFlags = featureFlags)
+ private val roundable = FakeRoundable(targetView = targetView)
@Test
fun defaultConfig_shouldNotHaveRoundedCorner() {
@@ -150,36 +148,36 @@
}
@Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_radius_maxed_to_height() {
whenever(targetView.height).thenReturn(10)
- featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(1f, 1f, SOURCE1)
assertCornerRadiiEquals(5f, 5f)
}
@Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_topRadius_maxed_to_height() {
whenever(targetView.height).thenReturn(5)
- featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(1f, 0f, SOURCE1)
assertCornerRadiiEquals(5f, 0f)
}
@Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_bottomRadius_maxed_to_height() {
whenever(targetView.height).thenReturn(5)
- featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(0f, 1f, SOURCE1)
assertCornerRadiiEquals(0f, 5f)
}
@Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_radii_kept() {
whenever(targetView.height).thenReturn(100)
- featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true)
roundable.requestRoundness(1f, 1f, SOURCE1)
assertCornerRadiiEquals(MAX_RADIUS, MAX_RADIUS)
@@ -193,14 +191,12 @@
class FakeRoundable(
targetView: View,
radius: Float = MAX_RADIUS,
- featureFlags: FeatureFlags
) : Roundable {
override val roundableState =
RoundableState(
targetView = targetView,
roundable = this,
maxRadius = radius,
- featureFlags = featureFlags
)
override val clipHeight: Int
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 104b751..2cf599a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -26,7 +26,7 @@
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 8cf64a5..4ff09d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpTree;
import static com.android.systemui.statusbar.notification.collection.ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index 3dcfcfa..b1180ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coalescer;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
index 362da0b..a652ad6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
@@ -20,7 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 4f1581c..a8be62b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -21,7 +21,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index 2ee016b..5ff7353 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -24,7 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 548ecde..58eec2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -18,7 +18,7 @@
import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
index 069eec2..56f16f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
@@ -21,7 +21,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
index 0b61a8d..22f6bdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
@@ -21,7 +21,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.util.mockito.eq
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index bad56a3..11996fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -18,7 +18,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
index 9a60272..eeabc74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
@@ -22,7 +22,7 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.log.logcatLogBuffer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
index f3094cd..170f651 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -80,7 +80,7 @@
assertThat(isPulseExpanding).isFalse()
withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
- .onPulseExpansionChanged(true)
+ .onPulseExpandingChanged(true)
runCurrent()
assertThat(isPulseExpanding).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index 360a373..47feccf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.shared.byIsAmbient
import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply
import com.android.systemui.statusbar.notification.shared.byIsPulsing
@@ -264,6 +265,7 @@
interface TestComponent : SysUITestComponent<StatusBarNotificationIconsInteractor> {
val activeNotificationListRepository: ActiveNotificationListRepository
+ val headsUpIconsInteractor: HeadsUpNotificationIconInteractor
val keyguardViewStateRepository: FakeNotificationsKeyguardViewStateRepository
val notificationListenerSettingsRepository: NotificationListenerSettingsRepository
@@ -336,6 +338,14 @@
.comparingElementsUsing(byIsLastMessageFromReply)
.doesNotContain(true)
}
+
+ @Test
+ fun filteredEntrySet_includesIsolatedIcon() =
+ testComponent.runTest {
+ val filteredSet by collectLastValue(underTest.statusBarNotifs)
+ headsUpIconsInteractor.setIsolatedIconNotificationKey("notif5")
+ assertThat(filteredSet).comparingElementsUsing(byKey).contains("notif5")
+ }
}
private val testIcons =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 349a35eb..c40401f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -399,4 +399,29 @@
assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1")
}
+
+ @Test
+ fun isolatedIcon_lastMessageIsFromReply_notNull() =
+ testComponent.runTest {
+ val icon: Icon = mock()
+ headsUpViewStateRepository.isolatedNotification.value = "notif1"
+ activeNotificationsRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ groupKey = "group",
+ statusBarIcon = icon,
+ isLastMessageFromReply = true,
+ )
+ )
+ }
+ .build()
+
+ val isolatedIcon by collectLastValue(underTest.isolatedIcon)
+ runCurrent()
+
+ assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1")
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index 04ffab3..60eea9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.interruption;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 7c8199e..9547af1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -27,8 +27,8 @@
import com.android.internal.logging.MetricsLogger
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.PluginManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index cf5b3cd..6faebf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.row;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index cb73108..a6bfaa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -21,7 +21,7 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static org.junit.Assert.assertEquals;
@@ -57,7 +57,6 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -568,9 +567,6 @@
NotificationEntry entry,
@InflationFlag int extraInflationFlags)
throws Exception {
- // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
- // set, but we do not want to override an existing value that is needed by a specific test.
- mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 32f0fe7..76470db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.row;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
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 c8dbdc5..d4300f0 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
@@ -17,6 +17,7 @@
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
@@ -28,8 +29,8 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
/** Tests for {@link NotificationShelf}. */
@SmallTest
@@ -53,7 +54,6 @@
MockitoAnnotations.initMocks(this)
mDependency.injectTestDependency(FeatureFlags::class.java, flags)
flags.set(Flags.SENSITIVE_REVEAL_ANIM, useSensitiveReveal)
- flags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS)
val root = FrameLayout(context)
shelf =
LayoutInflater.from(root.context)
@@ -72,6 +72,7 @@
@Test
fun testShadeWidth_BasedOnFractionToShade() {
+ mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
setFractionToShade(0f)
setOnLockscreen(true)
@@ -87,6 +88,7 @@
@Test
fun testShelfIsLong_WhenNotOnLockscreen() {
+ mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
setFractionToShade(0f)
setOnLockscreen(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 7558974..88662b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -38,6 +38,7 @@
import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
import android.metrics.LogMaker;
+import android.platform.test.annotations.DisableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -84,6 +85,7 @@
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -218,6 +220,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
initController(/* viewIsAttached= */ true);
@@ -238,6 +241,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -258,6 +262,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -285,6 +290,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerShowing_flagOff_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -306,6 +312,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerShowing_flagOn_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -327,6 +334,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerNotShowing_flagOff_showEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -348,6 +356,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testUpdateEmptyShadeView_bouncerNotShowing_flagOn_showEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
@@ -504,6 +513,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
initController(/* viewIsAttached= */ true);
mSeenNotificationsInteractor.setHasFilteredOutSeenNotifications(true);
@@ -545,6 +555,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() {
// GIVEN: Controller is attached, active notifications is empty,
// and mNotificationStackScrollLayout.onKeyguard() is true
@@ -561,6 +572,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() {
// GIVEN: Controller is attached, active notifications is not empty,
// and mNotificationStackScrollLayout.onKeyguard() is true
@@ -584,6 +596,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() {
// GIVEN: Controller is attached, active notifications is not empty,
// and mNotificationStackScrollLayout.onKeyguard() is false
@@ -607,6 +620,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() {
// GIVEN: Controller is attached, active notifications is empty,
// and mNotificationStackScrollLayout.onKeyguard() is false
@@ -623,6 +637,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() {
initController(/* viewIsAttached= */ true);
mController.onKeyguardTransitionChanged(
@@ -633,6 +648,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void updateEmptyShadeView_onKeyguardOccludedTransitionToAod_hidesView() {
initController(/* viewIsAttached= */ true);
mController.onKeyguardTransitionChanged(
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 ad7dee3..83ba684 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
@@ -51,6 +51,8 @@
import android.graphics.Insets;
import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
@@ -81,6 +83,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -191,7 +194,7 @@
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
- mStackScroller.setResetUserExpandedStatesRunnable(()->{});
+ mStackScroller.setResetUserExpandedStatesRunnable(() -> {});
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNotificationRoundnessManager())
@@ -309,7 +312,9 @@
public void updateEmptyView_dndSuppressing() {
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, true);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ true,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -319,7 +324,9 @@
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ false,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
}
@@ -328,10 +335,14 @@
public void updateEmptyView_noNotificationsToDndSuppressing() {
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mEmptyShadeView.willBeGone()).thenReturn(true);
- mStackScroller.updateEmptyShadeView(true, false);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ false,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.empty_shade_text);
- mStackScroller.updateEmptyShadeView(true, true);
+ mStackScroller.updateEmptyShadeView(/* visible = */ true,
+ /* areNotificationsHiddenInShade = */ true,
+ /* hasFilteredOutSeenNotifications = */ false);
verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
}
@@ -385,8 +396,8 @@
mStackScroller.setExpandedHeight(100f);
}
-
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void manageNotifications_visible() {
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -399,6 +410,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void clearAll_visible() {
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
@@ -411,6 +423,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testInflateFooterView() {
mStackScroller.inflateFooterView();
ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
@@ -444,7 +457,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(false, true, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
}
@Test
@@ -459,7 +472,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(false, false, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true);
}
@Test
@@ -474,7 +487,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(true, true, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, true);
}
@Test
@@ -490,7 +503,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(true, true, false);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, false);
}
@Test
@@ -505,7 +518,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(false, true, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
}
@Test
@@ -521,7 +534,7 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
- verify(mStackScroller).updateFooterView(true, false, true);
+ verify(mStackScroller, atLeastOnce()).updateFooterView(true, false, true);
}
@Test
@@ -529,7 +542,8 @@
mStackScroller.setCurrentUserSetup(true);
// add footer
- mStackScroller.inflateFooterView();
+ FooterView view = mock(FooterView.class);
+ mStackScroller.setFooterView(view);
// add notification
ExpandableNotificationRow row = createClearableRow();
@@ -545,6 +559,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void testReInflatesFooterViews() {
when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
clearInvocations(mStackScroller);
@@ -554,6 +569,16 @@
}
@Test
+ @EnableFlags(FooterViewRefactor.FLAG_NAME)
+ public void testReInflatesEmptyShadeView() {
+ when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
+ clearInvocations(mStackScroller);
+ mStackScroller.reinflateViews();
+ verify(mStackScroller, never()).setFooterView(any());
+ verify(mStackScroller).setEmptyShadeView(any());
+ }
+
+ @Test
public void testSetIsBeingDraggedResetsExposedMenu() {
mStackScroller.setIsBeingDragged(true);
verify(mNotificationSwipeHelper).resetExposedMenuView(true, true);
@@ -601,6 +626,8 @@
@Test
public void testClearNotifications_clearAllInProgress() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
ExpandableNotificationRow row = createClearableRow();
when(row.getEntry().hasFinishedInitialization()).thenReturn(true);
doReturn(true).when(mStackScroller).isVisible(row);
@@ -645,6 +672,8 @@
@Test
public void testAddNotificationUpdatesSpeedBumpIndex() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
// initial state calculated == 0
assertEquals(0, mStackScroller.getSpeedBumpIndex());
@@ -661,6 +690,8 @@
@Test
public void testAddAmbientNotificationNoSpeedBumpUpdate() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
// initial state calculated == 0
assertEquals(0, mStackScroller.getSpeedBumpIndex());
@@ -677,6 +708,8 @@
@Test
public void testRemoveNotificationUpdatesSpeedBump() {
+ mFeatureFlags.set(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE, false);
+
// initial state calculated == 0
assertEquals(0, mStackScroller.getSpeedBumpIndex());
@@ -872,6 +905,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void hasFilteredOutSeenNotifs_updateFooter() {
mStackScroller.setCurrentUserSetup(true);
@@ -887,6 +921,7 @@
}
@Test
+ @DisableFlags(FooterViewRefactor.FLAG_NAME)
public void hasFilteredOutSeenNotifs_updateEmptyShadeView() {
mStackScroller.setHasFilteredOutSeenNotifications(true);
mStackScroller.updateEmptyShadeView(true, false);
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 08ef477..f266f03 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
@@ -2,21 +2,28 @@
import android.annotation.DimenRes
import android.content.pm.PackageManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.res.R
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.RoundableState
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Expect
@@ -24,6 +31,7 @@
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Assume
import org.junit.Before
import org.junit.Rule
@@ -37,22 +45,26 @@
@SmallTest
class StackScrollAlgorithmTest : SysuiTestCase() {
- @JvmField @Rule
- var expect: Expect = Expect.create()
+ @JvmField @Rule var expect: Expect = Expect.create()
private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
private val hostView = FrameLayout(context)
private val stackScrollAlgorithm = StackScrollAlgorithm(context, hostView)
private val notificationRow = mock<ExpandableNotificationRow>()
+ private val notificationEntry = mock<NotificationEntry>()
private val dumpManager = mock<DumpManager>()
+ @OptIn(ExperimentalCoroutinesApi::class)
private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val notificationShelf = mock<NotificationShelf>()
- private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
- layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
- }
- private val footerView = FooterView(context, /*attrs=*/null)
- private val ambientState = AmbientState(
+ private val emptyShadeView =
+ EmptyShadeView(context, /* attrs= */ null).apply {
+ layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
+ }
+ private val footerView = FooterView(context, /*attrs=*/ null)
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private val ambientState =
+ AmbientState(
context,
dumpManager,
/* sectionProvider */ { _, _ -> false },
@@ -62,13 +74,14 @@
)
private val testableResources = mContext.getOrCreateTestableResources()
+ private val featureFlags = mock<FeatureFlagsClassic>()
private val maxPanelHeight =
mContext.resources.displayMetrics.heightPixels -
- px(R.dimen.notification_panel_margin_top) -
- px(R.dimen.notification_panel_margin_bottom)
+ px(R.dimen.notification_panel_margin_top) -
+ px(R.dimen.notification_panel_margin_bottom)
private fun px(@DimenRes id: Int): Float =
- testableResources.resources.getDimensionPixelSize(id).toFloat()
+ testableResources.resources.getDimensionPixelSize(id).toFloat()
private val bigGap = px(R.dimen.notification_section_divider_height)
private val smallGap = px(R.dimen.notification_section_divider_height_lockscreen)
@@ -76,9 +89,12 @@
@Before
fun setUp() {
Assume.assumeFalse(isTv())
-
+ mDependency.injectTestDependency(FeatureFlags::class.java, featureFlags)
whenever(notificationShelf.viewState).thenReturn(ExpandableViewState())
whenever(notificationRow.viewState).thenReturn(ExpandableViewState())
+ whenever(notificationRow.entry).thenReturn(notificationEntry)
+ whenever(notificationRow.roundableState)
+ .thenReturn(RoundableState(notificationRow, notificationRow, 0f))
ambientState.isSmallScreen = true
hostView.addView(notificationRow)
@@ -92,7 +108,7 @@
fun resetViewStates_defaultHun_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
- resetViewStates_hunYTranslationIsInset()
+ resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
}
@Test
@@ -103,18 +119,87 @@
}
@Test
+ @DisableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun resetViewStates_hunAnimatingAway_yTranslationIsInset() {
whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
- resetViewStates_hunYTranslationIsInset()
+ resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
}
@Test
+ @DisableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() {
whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
resetViewStates_stackMargin_changesHunYTranslation()
}
@Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_defaultHun_newHeadsUpAnim_yTranslationIsInset() {
+ whenever(notificationRow.isPinned).thenReturn(true)
+ whenever(notificationRow.isHeadsUp).thenReturn(true)
+ resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_defaultHunWithStackMargin_newHeadsUpAnim_changesHunYTranslation() {
+ whenever(notificationRow.isPinned).thenReturn(true)
+ whenever(notificationRow.isHeadsUp).thenReturn(true)
+ resetViewStates_stackMargin_changesHunYTranslation()
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_defaultHun_showingQS_newHeadsUpAnim_hunTranslatedToMax() {
+ // Given: the shade is open and scrolled to the bottom to show the QuickSettings
+ val maxHunTranslation = 2000f
+ ambientState.maxHeadsUpTranslation = maxHunTranslation
+ ambientState.setLayoutMinHeight(2500) // Mock the height of shade
+ ambientState.stackY = 2500f // Scroll over the max translation
+ stackScrollAlgorithm.setIsExpanded(true) // Mark the shade open
+ whenever(notificationRow.mustStayOnScreen()).thenReturn(true)
+ whenever(notificationRow.isHeadsUp).thenReturn(true)
+ whenever(notificationRow.isAboveShelf).thenReturn(true)
+
+ resetViewStates_hunYTranslationIs(maxHunTranslation)
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_hunAnimatingAway_showingQS_newHeadsUpAnim_hunTranslatedToBottomOfScreen() {
+ // Given: the shade is open and scrolled to the bottom to show the QuickSettings
+ val bottomOfScreen = 2600f
+ val maxHunTranslation = 2000f
+ ambientState.maxHeadsUpTranslation = maxHunTranslation
+ ambientState.setLayoutMinHeight(2500) // Mock the height of shade
+ ambientState.stackY = 2500f // Scroll over the max translation
+ stackScrollAlgorithm.setIsExpanded(true) // Mark the shade open
+ stackScrollAlgorithm.setHeadsUpAppearHeightBottom(bottomOfScreen.toInt())
+ whenever(notificationRow.mustStayOnScreen()).thenReturn(true)
+ whenever(notificationRow.isHeadsUp).thenReturn(true)
+ whenever(notificationRow.isAboveShelf).thenReturn(true)
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+ resetViewStates_hunYTranslationIs(
+ expected = bottomOfScreen + stackScrollAlgorithm.mHeadsUpAppearStartAboveScreen
+ )
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_hunAnimatingAway_newHeadsUpAnim_hunTranslatedToTopOfScreen() {
+ val topMargin = 100f
+ ambientState.maxHeadsUpTranslation = 2000f
+ ambientState.stackTopMargin = topMargin.toInt()
+ whenever(notificationRow.intrinsicHeight).thenReturn(100)
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+ resetViewStates_hunYTranslationIs(
+ expected = -topMargin - stackScrollAlgorithm.mHeadsUpAppearStartAboveScreen
+ )
+ }
+
+ @Test
fun resetViewStates_hunAnimatingAway_bottomNotClipped() {
whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
@@ -136,6 +221,7 @@
}
@Test
+ @DisableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() {
val topHun = mockExpandableNotificationRow()
val bottomHun = mockExpandableNotificationRow()
@@ -156,7 +242,7 @@
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
val marginBottom =
- context.resources.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom)
+ context.resources.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom)
val fullHeight = ambientState.layoutMaxHeight + marginBottom - ambientState.stackY
val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
assertThat(emptyShadeView.viewState.yTranslation).isEqualTo(centeredY)
@@ -174,33 +260,37 @@
assertThat(notificationRow.viewState.alpha).isEqualTo(1f)
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun resetViewStates_expansionChanging_notificationBecomesTransparent() {
whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
resetViewStates_expansionChanging_notificationAlphaUpdated(
- expansionFraction = 0.25f,
- expectedAlpha = 0.0f
+ expansionFraction = 0.25f,
+ expectedAlpha = 0.0f
)
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun resetViewStates_expansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
resetViewStates_expansionChanging_notificationAlphaUpdated(
- expansionFraction = 0.85f,
- expectedAlpha = 0.0f
+ expansionFraction = 0.85f,
+ expectedAlpha = 0.0f
)
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun resetViewStates_expansionChanging_notificationAlphaUpdated() {
whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
resetViewStates_expansionChanging_notificationAlphaUpdated(
- expansionFraction = 0.6f,
- expectedAlpha = getContentAlpha(0.6f)
+ expansionFraction = 0.6f,
+ expectedAlpha = getContentAlpha(0.6f)
)
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun resetViewStates_largeScreen_expansionChanging_alphaUpdated_largeScreenValue() {
val expansionFraction = 0.6f
@@ -216,13 +306,14 @@
)
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun expansionChanging_largeScreen_bouncerInTransit_alphaUpdated_bouncerValues() {
ambientState.isSmallScreen = false
whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
resetViewStates_expansionChanging_notificationAlphaUpdated(
- expansionFraction = 0.95f,
- expectedAlpha = aboutToShowBouncerProgress(0.95f),
+ expansionFraction = 0.95f,
+ expectedAlpha = aboutToShowBouncerProgress(0.95f),
)
}
@@ -235,10 +326,8 @@
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
- verify(notificationShelf).updateState(
- /* algorithmState= */any(),
- /* ambientState= */eq(ambientState)
- )
+ verify(notificationShelf)
+ .updateState(/* algorithmState= */ any(), /* ambientState= */ eq(ambientState))
}
@Test
@@ -397,22 +486,31 @@
@Test
fun getGapForLocation_onLockscreen_returnsSmallGap() {
- val gap = stackScrollAlgorithm.getGapForLocation(
- /* fractionToShade= */ 0f, /* onKeyguard= */ true)
+ val gap =
+ stackScrollAlgorithm.getGapForLocation(
+ /* fractionToShade= */ 0f,
+ /* onKeyguard= */ true
+ )
assertThat(gap).isEqualTo(smallGap)
}
@Test
fun getGapForLocation_goingToShade_interpolatesGap() {
- val gap = stackScrollAlgorithm.getGapForLocation(
- /* fractionToShade= */ 0.5f, /* onKeyguard= */ true)
+ val gap =
+ stackScrollAlgorithm.getGapForLocation(
+ /* fractionToShade= */ 0.5f,
+ /* onKeyguard= */ true
+ )
assertThat(gap).isEqualTo(smallGap * 0.5f + bigGap * 0.5f)
}
@Test
fun getGapForLocation_notOnLockscreen_returnsBigGap() {
- val gap = stackScrollAlgorithm.getGapForLocation(
- /* fractionToShade= */ 0f, /* onKeyguard= */ false)
+ val gap =
+ stackScrollAlgorithm.getGapForLocation(
+ /* fractionToShade= */ 0f,
+ /* onKeyguard= */ false
+ )
assertThat(gap).isEqualTo(bigGap)
}
@@ -469,12 +567,14 @@
val expandableViewState = ExpandableViewState()
expandableViewState.headsUpIsVisible = false
- stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
- /* isShadeExpanded= */ true,
- /* mustStayOnScreen= */ true,
- /* isViewEndVisible= */ true,
- /* viewEnd= */ 0f,
- /* maxHunY= */ 10f)
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(
+ expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 0f,
+ /* maxHunY= */ 10f
+ )
assertTrue(expandableViewState.headsUpIsVisible)
}
@@ -484,12 +584,14 @@
val expandableViewState = ExpandableViewState()
expandableViewState.headsUpIsVisible = true
- stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
- /* isShadeExpanded= */ true,
- /* mustStayOnScreen= */ true,
- /* isViewEndVisible= */ true,
- /* viewEnd= */ 10f,
- /* maxHunY= */ 0f)
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(
+ expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 0f
+ )
assertFalse(expandableViewState.headsUpIsVisible)
}
@@ -499,12 +601,14 @@
val expandableViewState = ExpandableViewState()
expandableViewState.headsUpIsVisible = true
- stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
- /* isShadeExpanded= */ false,
- /* mustStayOnScreen= */ true,
- /* isViewEndVisible= */ true,
- /* viewEnd= */ 10f,
- /* maxHunY= */ 1f)
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(
+ expandableViewState,
+ /* isShadeExpanded= */ false,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 1f
+ )
assertTrue(expandableViewState.headsUpIsVisible)
}
@@ -514,12 +618,14 @@
val expandableViewState = ExpandableViewState()
expandableViewState.headsUpIsVisible = true
- stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
- /* isShadeExpanded= */ true,
- /* mustStayOnScreen= */ false,
- /* isViewEndVisible= */ true,
- /* viewEnd= */ 10f,
- /* maxHunY= */ 1f)
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(
+ expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ false,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 1f
+ )
assertTrue(expandableViewState.headsUpIsVisible)
}
@@ -529,12 +635,14 @@
val expandableViewState = ExpandableViewState()
expandableViewState.headsUpIsVisible = true
- stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
- /* isShadeExpanded= */ true,
- /* mustStayOnScreen= */ true,
- /* isViewEndVisible= */ false,
- /* viewEnd= */ 10f,
- /* maxHunY= */ 1f)
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(
+ expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ false,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 1f
+ )
assertTrue(expandableViewState.headsUpIsVisible)
}
@@ -544,9 +652,12 @@
val expandableViewState = ExpandableViewState()
expandableViewState.yTranslation = 50f
- stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
- /* stackTranslation= */ 0f,
- /* collapsedHeight= */ 1f, expandableViewState)
+ stackScrollAlgorithm.clampHunToTop(
+ /* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 1f,
+ expandableViewState
+ )
// qqs (10 + 0) < viewY (50)
assertEquals(50f, expandableViewState.yTranslation)
@@ -557,9 +668,12 @@
val expandableViewState = ExpandableViewState()
expandableViewState.yTranslation = -10f
- stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
- /* stackTranslation= */ 0f,
- /* collapsedHeight= */ 1f, expandableViewState)
+ stackScrollAlgorithm.clampHunToTop(
+ /* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 1f,
+ expandableViewState
+ )
// qqs (10 + 0) > viewY (-10)
assertEquals(10f, expandableViewState.yTranslation)
@@ -571,9 +685,12 @@
expandableViewState.height = 20
expandableViewState.yTranslation = -100f
- stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
- /* stackTranslation= */ 0f,
- /* collapsedHeight= */ 10f, expandableViewState)
+ stackScrollAlgorithm.clampHunToTop(
+ /* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 10f,
+ expandableViewState
+ )
// newTranslation = max(10, -100) = 10
// distToRealY = 10 - (-100f) = 110
@@ -587,9 +704,12 @@
expandableViewState.height = 20
expandableViewState.yTranslation = 5f
- stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
- /* stackTranslation= */ 0f,
- /* collapsedHeight= */ 10f, expandableViewState)
+ stackScrollAlgorithm.clampHunToTop(
+ /* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 10f,
+ expandableViewState
+ )
// newTranslation = max(10, 5) = 10
// distToRealY = 10 - 5 = 5
@@ -599,41 +719,49 @@
@Test
fun computeCornerRoundnessForPinnedHun_stackBelowScreen_round() {
- val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ val currentRoundness =
+ stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
/* hostViewHeight= */ 100f,
/* stackY= */ 110f,
/* viewMaxHeight= */ 20f,
- /* originalCornerRoundness= */ 0f)
+ /* originalCornerRoundness= */ 0f
+ )
assertEquals(1f, currentRoundness)
}
@Test
fun computeCornerRoundnessForPinnedHun_stackAboveScreenBelowPinPoint_halfRound() {
- val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ val currentRoundness =
+ stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
/* hostViewHeight= */ 100f,
/* stackY= */ 90f,
/* viewMaxHeight= */ 20f,
- /* originalCornerRoundness= */ 0f)
+ /* originalCornerRoundness= */ 0f
+ )
assertEquals(0.5f, currentRoundness)
}
@Test
fun computeCornerRoundnessForPinnedHun_stackAbovePinPoint_notRound() {
- val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ val currentRoundness =
+ stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
/* hostViewHeight= */ 100f,
/* stackY= */ 0f,
/* viewMaxHeight= */ 20f,
- /* originalCornerRoundness= */ 0f)
+ /* originalCornerRoundness= */ 0f
+ )
assertEquals(0f, currentRoundness)
}
@Test
fun computeCornerRoundnessForPinnedHun_originallyRoundAndStackAbovePinPoint_round() {
- val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ val currentRoundness =
+ stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
/* hostViewHeight= */ 100f,
/* stackY= */ 0f,
/* viewMaxHeight= */ 20f,
- /* originalCornerRoundness= */ 1f)
+ /* originalCornerRoundness= */ 1f
+ )
assertEquals(1f, currentRoundness)
}
@@ -642,23 +770,20 @@
// Given: shade is opened, yTranslation of HUN is 0,
// the height of HUN equals to the height of QQS Panel,
// and HUN fully overlaps with QQS Panel
- ambientState.stackTranslation = px(R.dimen.qqs_layout_margin_top) +
- px(R.dimen.qqs_layout_padding_bottom)
- val childHunView = createHunViewMock(
- isShadeOpen = true,
- fullyVisible = false,
- headerVisibleAmount = 1f
- )
+ ambientState.stackTranslation =
+ px(R.dimen.qqs_layout_margin_top) + px(R.dimen.qqs_layout_padding_bottom)
+ val childHunView =
+ createHunViewMock(isShadeOpen = true, fullyVisible = false, headerVisibleAmount = 1f)
val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
algorithmState.visibleChildren.add(childHunView)
// When: updateChildZValue() is called for the top HUN
stackScrollAlgorithm.updateChildZValue(
- /* i= */ 0,
- /* childrenOnTop= */ 0.0f,
- /* StackScrollAlgorithmState= */ algorithmState,
- /* ambientState= */ ambientState,
- /* shouldElevateHun= */ true
+ /* i= */ 0,
+ /* childrenOnTop= */ 0.0f,
+ /* StackScrollAlgorithmState= */ algorithmState,
+ /* ambientState= */ ambientState,
+ /* shouldElevateHun= */ true
)
// Then: full shadow would be applied
@@ -670,13 +795,10 @@
// Given: shade is opened, yTranslation of HUN is greater than 0,
// the height of HUN is equal to the height of QQS Panel,
// and HUN partially overlaps with QQS Panel
- ambientState.stackTranslation = px(R.dimen.qqs_layout_margin_top) +
- px(R.dimen.qqs_layout_padding_bottom)
- val childHunView = createHunViewMock(
- isShadeOpen = true,
- fullyVisible = false,
- headerVisibleAmount = 1f
- )
+ ambientState.stackTranslation =
+ px(R.dimen.qqs_layout_margin_top) + px(R.dimen.qqs_layout_padding_bottom)
+ val childHunView =
+ createHunViewMock(isShadeOpen = true, fullyVisible = false, headerVisibleAmount = 1f)
// Use half of the HUN's height as overlap
childHunView.viewState.yTranslation = (childHunView.viewState.height + 1 shr 1).toFloat()
val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
@@ -684,17 +806,17 @@
// When: updateChildZValue() is called for the top HUN
stackScrollAlgorithm.updateChildZValue(
- /* i= */ 0,
- /* childrenOnTop= */ 0.0f,
- /* StackScrollAlgorithmState= */ algorithmState,
- /* ambientState= */ ambientState,
- /* shouldElevateHun= */ true
+ /* i= */ 0,
+ /* childrenOnTop= */ 0.0f,
+ /* StackScrollAlgorithmState= */ algorithmState,
+ /* ambientState= */ ambientState,
+ /* shouldElevateHun= */ true
)
// Then: HUN should have shadow, but not as full size
assertThat(childHunView.viewState.zTranslation).isGreaterThan(0.0f)
assertThat(childHunView.viewState.zTranslation)
- .isLessThan(px(R.dimen.heads_up_pinned_elevation))
+ .isLessThan(px(R.dimen.heads_up_pinned_elevation))
}
@Test
@@ -702,28 +824,25 @@
// 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,
// and HUN doesn't overlap with QQS Panel
- ambientState.stackTranslation = px(R.dimen.qqs_layout_margin_top) +
- px(R.dimen.qqs_layout_padding_bottom)
+ ambientState.stackTranslation =
+ px(R.dimen.qqs_layout_margin_top) + px(R.dimen.qqs_layout_padding_bottom)
// Mock the height of shade
ambientState.setLayoutMinHeight(1000)
- val childHunView = createHunViewMock(
- isShadeOpen = true,
- fullyVisible = true,
- headerVisibleAmount = 1f
- )
+ val childHunView =
+ createHunViewMock(isShadeOpen = true, fullyVisible = true, headerVisibleAmount = 1f)
// HUN doesn't overlap with QQS Panel
- childHunView.viewState.yTranslation = ambientState.topPadding +
- ambientState.stackTranslation
+ childHunView.viewState.yTranslation =
+ ambientState.topPadding + ambientState.stackTranslation
val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
algorithmState.visibleChildren.add(childHunView)
// When: updateChildZValue() is called for the top HUN
stackScrollAlgorithm.updateChildZValue(
- /* i= */ 0,
- /* childrenOnTop= */ 0.0f,
- /* StackScrollAlgorithmState= */ algorithmState,
- /* ambientState= */ ambientState,
- /* shouldElevateHun= */ true
+ /* i= */ 0,
+ /* childrenOnTop= */ 0.0f,
+ /* StackScrollAlgorithmState= */ algorithmState,
+ /* ambientState= */ ambientState,
+ /* shouldElevateHun= */ true
)
// Then: HUN should not have shadow
@@ -737,11 +856,8 @@
ambientState.stackTranslation = -ambientState.topPadding
// Mock the height of shade
ambientState.setLayoutMinHeight(1000)
- val childHunView = createHunViewMock(
- isShadeOpen = false,
- fullyVisible = false,
- headerVisibleAmount = 0f
- )
+ val childHunView =
+ createHunViewMock(isShadeOpen = false, fullyVisible = false, headerVisibleAmount = 0f)
childHunView.viewState.yTranslation = 0f
// Shade is closed, thus childHunView's headerVisibleAmount is 0
childHunView.headerVisibleAmount = 0f
@@ -750,11 +866,11 @@
// When: updateChildZValue() is called for the top HUN
stackScrollAlgorithm.updateChildZValue(
- /* i= */ 0,
- /* childrenOnTop= */ 0.0f,
- /* StackScrollAlgorithmState= */ algorithmState,
- /* ambientState= */ ambientState,
- /* shouldElevateHun= */ true
+ /* i= */ 0,
+ /* childrenOnTop= */ 0.0f,
+ /* StackScrollAlgorithmState= */ algorithmState,
+ /* ambientState= */ ambientState,
+ /* shouldElevateHun= */ true
)
// Then: HUN should have full shadow
@@ -768,11 +884,8 @@
ambientState.stackTranslation = -ambientState.topPadding
// Mock the height of shade
ambientState.setLayoutMinHeight(1000)
- val childHunView = createHunViewMock(
- isShadeOpen = false,
- fullyVisible = false,
- headerVisibleAmount = 0.5f
- )
+ val childHunView =
+ createHunViewMock(isShadeOpen = false, fullyVisible = false, headerVisibleAmount = 0.5f)
childHunView.viewState.yTranslation = 0f
// Shade is being opened, thus childHunView's headerVisibleAmount is between 0 and 1
// use 0.5 as headerVisibleAmount here
@@ -782,17 +895,17 @@
// When: updateChildZValue() is called for the top HUN
stackScrollAlgorithm.updateChildZValue(
- /* i= */ 0,
- /* childrenOnTop= */ 0.0f,
- /* StackScrollAlgorithmState= */ algorithmState,
- /* ambientState= */ ambientState,
- /* shouldElevateHun= */ true
+ /* i= */ 0,
+ /* childrenOnTop= */ 0.0f,
+ /* StackScrollAlgorithmState= */ algorithmState,
+ /* ambientState= */ ambientState,
+ /* shouldElevateHun= */ true
)
// Then: HUN should have shadow, but not as full size
assertThat(childHunView.viewState.zTranslation).isGreaterThan(0.0f)
assertThat(childHunView.viewState.zTranslation)
- .isLessThan(px(R.dimen.heads_up_pinned_elevation))
+ .isLessThan(px(R.dimen.heads_up_pinned_elevation))
}
@Test
@@ -862,134 +975,174 @@
// stackScrollAlgorithm.resetViewStates is called.
ambientState.dozeAmount = 0.5f
setExpansionFractionWithoutShelfDuringAodToLockScreen(
- ambientState,
- algorithmState,
- fraction = 0.5f
+ ambientState,
+ algorithmState,
+ fraction = 0.5f
)
stackScrollAlgorithm.resetViewStates(ambientState, 0)
// Then: pulsingNotificationView should show at full height
assertEquals(
- stackScrollAlgorithm.getMaxAllowedChildHeight(pulsingNotificationView),
- pulsingNotificationView.viewState.height
+ stackScrollAlgorithm.getMaxAllowedChildHeight(pulsingNotificationView),
+ pulsingNotificationView.viewState.height
)
// After: reset dozeAmount and expansionFraction
ambientState.dozeAmount = 0f
setExpansionFractionWithoutShelfDuringAodToLockScreen(
- ambientState,
- algorithmState,
- fraction = 1f
+ ambientState,
+ algorithmState,
+ fraction = 1f
)
}
// region shouldPinHunToBottomOfExpandedQs
@Test
fun shouldHunBeVisibleWhenScrolled_mustStayOnScreenFalse_false() {
- assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
- /* mustStayOnScreen= */false,
- /* headsUpIsVisible= */false,
- /* showingPulsing= */false,
- /* isOnKeyguard=*/false,
- /*headsUpOnKeyguard=*/false
- )).isFalse()
+ assertThat(
+ stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
+ /* mustStayOnScreen= */ false,
+ /* headsUpIsVisible= */ false,
+ /* showingPulsing= */ false,
+ /* isOnKeyguard=*/ false,
+ /*headsUpOnKeyguard=*/ false
+ )
+ )
+ .isFalse()
}
@Test
fun shouldPinHunToBottomOfExpandedQs_headsUpIsVisible_false() {
- assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
- /* mustStayOnScreen= */true,
- /* headsUpIsVisible= */true,
- /* showingPulsing= */false,
- /* isOnKeyguard=*/false,
- /*headsUpOnKeyguard=*/false
- )).isFalse()
+ assertThat(
+ stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
+ /* mustStayOnScreen= */ true,
+ /* headsUpIsVisible= */ true,
+ /* showingPulsing= */ false,
+ /* isOnKeyguard=*/ false,
+ /*headsUpOnKeyguard=*/ false
+ )
+ )
+ .isFalse()
}
@Test
fun shouldHunBeVisibleWhenScrolled_showingPulsing_false() {
- assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
- /* mustStayOnScreen= */true,
- /* headsUpIsVisible= */false,
- /* showingPulsing= */true,
- /* isOnKeyguard=*/false,
- /* headsUpOnKeyguard= */false
- )).isFalse()
+ assertThat(
+ stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
+ /* mustStayOnScreen= */ true,
+ /* headsUpIsVisible= */ false,
+ /* showingPulsing= */ true,
+ /* isOnKeyguard=*/ false,
+ /* headsUpOnKeyguard= */ false
+ )
+ )
+ .isFalse()
}
@Test
fun shouldHunBeVisibleWhenScrolled_isOnKeyguard_false() {
- assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
- /* mustStayOnScreen= */true,
- /* headsUpIsVisible= */false,
- /* showingPulsing= */false,
- /* isOnKeyguard=*/true,
- /* headsUpOnKeyguard= */false
- )).isFalse()
+ assertThat(
+ stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
+ /* mustStayOnScreen= */ true,
+ /* headsUpIsVisible= */ false,
+ /* showingPulsing= */ false,
+ /* isOnKeyguard=*/ true,
+ /* headsUpOnKeyguard= */ false
+ )
+ )
+ .isFalse()
}
@Test
fun shouldHunBeVisibleWhenScrolled_isNotOnKeyguard_true() {
- assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
- /* mustStayOnScreen= */true,
- /* headsUpIsVisible= */false,
- /* showingPulsing= */false,
- /* isOnKeyguard=*/false,
- /* headsUpOnKeyguard= */false
- )).isTrue()
+ assertThat(
+ stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
+ /* mustStayOnScreen= */ true,
+ /* headsUpIsVisible= */ false,
+ /* showingPulsing= */ false,
+ /* isOnKeyguard=*/ false,
+ /* headsUpOnKeyguard= */ false
+ )
+ )
+ .isTrue()
}
@Test
fun shouldHunBeVisibleWhenScrolled_headsUpOnKeyguard_true() {
- assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
- /* mustStayOnScreen= */true,
- /* headsUpIsVisible= */false,
- /* showingPulsing= */false,
- /* isOnKeyguard=*/true,
- /* headsUpOnKeyguard= */true
- )).isTrue()
+ assertThat(
+ stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled(
+ /* mustStayOnScreen= */ true,
+ /* headsUpIsVisible= */ false,
+ /* showingPulsing= */ false,
+ /* isOnKeyguard=*/ true,
+ /* headsUpOnKeyguard= */ true
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun shouldHunAppearFromBottom_hunAtMaxHunTranslation() {
+ ambientState.maxHeadsUpTranslation = 400f
+ val viewState =
+ ExpandableViewState().apply {
+ height = 100
+ yTranslation = ambientState.maxHeadsUpTranslation - height // move it to the max
+ }
+
+ assertTrue(stackScrollAlgorithm.shouldHunAppearFromBottom(ambientState, viewState))
+ }
+
+ @Test
+ fun shouldHunAppearFromBottom_hunBelowMaxHunTranslation() {
+ ambientState.maxHeadsUpTranslation = 400f
+ val viewState =
+ ExpandableViewState().apply {
+ height = 100
+ yTranslation =
+ ambientState.maxHeadsUpTranslation - height - 1 // move it below the max
+ }
+
+ assertFalse(stackScrollAlgorithm.shouldHunAppearFromBottom(ambientState, viewState))
}
// endregion
private fun createHunViewMock(
- isShadeOpen: Boolean,
- fullyVisible: Boolean,
- headerVisibleAmount: Float
+ isShadeOpen: Boolean,
+ fullyVisible: Boolean,
+ headerVisibleAmount: Float
) =
- mock<ExpandableNotificationRow>().apply {
- val childViewStateMock = createHunChildViewState(isShadeOpen, fullyVisible)
- whenever(this.viewState).thenReturn(childViewStateMock)
+ mock<ExpandableNotificationRow>().apply {
+ val childViewStateMock = createHunChildViewState(isShadeOpen, fullyVisible)
+ whenever(this.viewState).thenReturn(childViewStateMock)
- whenever(this.mustStayOnScreen()).thenReturn(true)
- whenever(this.headerVisibleAmount).thenReturn(headerVisibleAmount)
- }
-
+ whenever(this.mustStayOnScreen()).thenReturn(true)
+ whenever(this.headerVisibleAmount).thenReturn(headerVisibleAmount)
+ }
private fun createHunChildViewState(isShadeOpen: Boolean, fullyVisible: Boolean) =
- ExpandableViewState().apply {
- // Mock the HUN's height with ambientState.topPadding +
- // ambientState.stackTranslation
- height = (ambientState.topPadding + ambientState.stackTranslation).toInt()
- if (isShadeOpen && fullyVisible) {
- yTranslation =
- ambientState.topPadding + ambientState.stackTranslation
- } else {
- yTranslation = 0f
- }
- headsUpIsVisible = fullyVisible
+ ExpandableViewState().apply {
+ // Mock the HUN's height with ambientState.topPadding +
+ // ambientState.stackTranslation
+ height = (ambientState.topPadding + ambientState.stackTranslation).toInt()
+ if (isShadeOpen && fullyVisible) {
+ yTranslation = ambientState.topPadding + ambientState.stackTranslation
+ } else {
+ yTranslation = 0f
}
+ headsUpIsVisible = fullyVisible
+ }
- private fun createPulsingViewMock(
- ) =
- mock<ExpandableNotificationRow>().apply {
- whenever(this.viewState).thenReturn(ExpandableViewState())
- whenever(this.showingPulsing()).thenReturn(true)
- }
+ private fun createPulsingViewMock() =
+ mock<ExpandableNotificationRow>().apply {
+ whenever(this.viewState).thenReturn(ExpandableViewState())
+ whenever(this.showingPulsing()).thenReturn(true)
+ }
private fun setExpansionFractionWithoutShelfDuringAodToLockScreen(
- ambientState: AmbientState,
- algorithmState: StackScrollAlgorithm.StackScrollAlgorithmState,
- fraction: Float
+ ambientState: AmbientState,
+ algorithmState: StackScrollAlgorithm.StackScrollAlgorithmState,
+ fraction: Float
) {
// showingShelf: false
algorithmState.firstViewInShelf = null
@@ -1002,11 +1155,10 @@
ambientState.stackHeight = ambientState.stackEndHeight * fraction
}
- private fun resetViewStates_hunYTranslationIsInset() {
+ private fun resetViewStates_hunYTranslationIs(expected: Float) {
stackScrollAlgorithm.resetViewStates(ambientState, 0)
- assertThat(notificationRow.viewState.yTranslation)
- .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+ assertThat(notificationRow.viewState.yTranslation).isEqualTo(expected)
}
private fun resetViewStates_stackMargin_changesHunYTranslation() {
@@ -1025,13 +1177,13 @@
}
private fun resetViewStates_hunsOverlapping_bottomHunClipped(
- topHun: ExpandableNotificationRow,
- bottomHun: ExpandableNotificationRow
+ topHun: ExpandableNotificationRow,
+ bottomHun: ExpandableNotificationRow
) {
- val topHunHeight = mContext.resources.getDimensionPixelSize(
- R.dimen.notification_content_min_height)
- val bottomHunHeight = mContext.resources.getDimensionPixelSize(
- R.dimen.notification_max_heads_up_height)
+ val topHunHeight =
+ mContext.resources.getDimensionPixelSize(R.dimen.notification_content_min_height)
+ val bottomHunHeight =
+ mContext.resources.getDimensionPixelSize(R.dimen.notification_max_heads_up_height)
whenever(topHun.intrinsicHeight).thenReturn(topHunHeight)
whenever(bottomHun.intrinsicHeight).thenReturn(bottomHunHeight)
@@ -1054,8 +1206,8 @@
}
private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
- expansionFraction: Float,
- expectedAlpha: Float,
+ expansionFraction: Float,
+ expectedAlpha: Float,
) {
ambientState.isExpansionChanging = true
ambientState.expansionFraction = expansionFraction
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
new file mode 100644
index 0000000..5a57035
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.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.systemui.statusbar.notification.stack
+
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.any
+import org.mockito.Mockito.description
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+
+private const val VIEW_HEIGHT = 100
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class StackStateAnimatorTest : SysuiTestCase() {
+
+ private lateinit var stackStateAnimator: StackStateAnimator
+ private val stackScroller: NotificationStackScrollLayout = mock()
+ private val view: ExpandableView = mock()
+ private val viewState: ExpandableViewState =
+ ExpandableViewState().apply { height = VIEW_HEIGHT }
+ private val runnableCaptor: ArgumentCaptor<Runnable> = argumentCaptor()
+ @Before
+ fun setUp() {
+ whenever(stackScroller.context).thenReturn(context)
+ whenever(view.viewState).thenReturn(viewState)
+ stackStateAnimator = StackStateAnimator(stackScroller)
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim() {
+ val topMargin = 50f
+ val expectedStartY = -topMargin - stackStateAnimator.mHeadsUpAppearStartAboveScreen
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR)
+ stackStateAnimator.setStackTopMargin(topMargin.toInt())
+
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view).setActualHeight(VIEW_HEIGHT, false)
+ verify(view, description("should animate from the top")).translationY = expectedStartY
+ verify(view)
+ .performAddAnimation(
+ /* delay= */ 0L,
+ /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+ /* isHeadsUpAppear= */ true,
+ /* onEndRunnable= */ null
+ )
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim() {
+ val screenHeight = 2000f
+ val expectedStartY = screenHeight + stackStateAnimator.mHeadsUpAppearStartAboveScreen
+ val event =
+ AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR).apply {
+ headsUpFromBottom = true
+ }
+ stackStateAnimator.setHeadsUpAppearHeightBottom(screenHeight.toInt())
+
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view).setActualHeight(VIEW_HEIGHT, false)
+ verify(view, description("should animate from the bottom")).translationY = expectedStartY
+ verify(view)
+ .performAddAnimation(
+ /* delay= */ 0L,
+ /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+ /* isHeadsUpAppear= */ true,
+ /* onEndRunnable= */ null
+ )
+ }
+
+ @Test
+ fun startAnimationForEvents_startsHeadsUpDisappearAnim() {
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view)
+ .performRemoveAnimation(
+ /* duration= */ eq(ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()),
+ /* delay= */ eq(0L),
+ /* translationDirection= */ eq(0f),
+ /* isHeadsUpAnimation= */ eq(true),
+ /* onStartedRunnable= */ any(),
+ /* onFinishedRunnable= */ runnableCaptor.capture(),
+ /* animationListener= */ any()
+ )
+
+ runnableCaptor.value.run() // execute the end runnable
+
+ verify(view, description("should be called at the end of the animation"))
+ .removeFromTransientContainer()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
index da543d4..cd6bb5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
@@ -17,10 +17,10 @@
package com.android.systemui.statusbar.notification.stack
import android.testing.AndroidTestingRunner
-import android.util.Log
-import android.util.Log.TerribleFailureHandler
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.assertDoesNotLogWtf
+import com.android.systemui.log.assertLogsWtf
import kotlin.math.log2
import kotlin.math.sqrt
import org.junit.Assert
@@ -32,61 +32,36 @@
class ViewStateTest : SysuiTestCase() {
private val viewState = ViewState()
- private var wtfHandler: TerribleFailureHandler? = null
- private var wtfCount = 0
-
@Suppress("DIVISION_BY_ZERO")
@Test
fun testWtfs() {
- interceptWtfs()
-
// Setting valid values doesn't cause any wtfs.
- viewState.alpha = 0.1f
- viewState.xTranslation = 0f
- viewState.yTranslation = 10f
- viewState.zTranslation = 20f
- viewState.scaleX = 0.5f
- viewState.scaleY = 0.25f
-
- expectWtfs(0)
+ assertDoesNotLogWtf {
+ viewState.alpha = 0.1f
+ viewState.xTranslation = 0f
+ viewState.yTranslation = 10f
+ viewState.zTranslation = 20f
+ viewState.scaleX = 0.5f
+ viewState.scaleY = 0.25f
+ }
// Setting NaN values leads to wtfs being logged, and the value not being changed.
- viewState.alpha = 0.0f / 0.0f
- expectWtfs(1)
+ assertLogsWtf { viewState.alpha = 0.0f / 0.0f }
Assert.assertEquals(viewState.alpha, 0.1f)
- viewState.xTranslation = Float.NaN
- expectWtfs(2)
+ assertLogsWtf { viewState.xTranslation = Float.NaN }
Assert.assertEquals(viewState.xTranslation, 0f)
- viewState.yTranslation = log2(-10.0).toFloat()
- expectWtfs(3)
+ assertLogsWtf { viewState.yTranslation = log2(-10.0).toFloat() }
Assert.assertEquals(viewState.yTranslation, 10f)
- viewState.zTranslation = sqrt(-1.0).toFloat()
- expectWtfs(4)
+ assertLogsWtf { viewState.zTranslation = sqrt(-1.0).toFloat() }
Assert.assertEquals(viewState.zTranslation, 20f)
- viewState.scaleX = Float.POSITIVE_INFINITY + Float.NEGATIVE_INFINITY
- expectWtfs(5)
+ assertLogsWtf { viewState.scaleX = Float.POSITIVE_INFINITY + Float.NEGATIVE_INFINITY }
Assert.assertEquals(viewState.scaleX, 0.5f)
- viewState.scaleY = Float.POSITIVE_INFINITY * 0
- expectWtfs(6)
+ assertLogsWtf { viewState.scaleY = Float.POSITIVE_INFINITY * 0 }
Assert.assertEquals(viewState.scaleY, 0.25f)
}
-
- private fun interceptWtfs() {
- wtfCount = 0
- wtfHandler =
- Log.setWtfHandler { _: String?, e: Log.TerribleFailure, _: Boolean ->
- Log.e("ViewStateTest", "Observed WTF: $e")
- wtfCount++
- }
- }
-
- private fun expectWtfs(expectedWtfCount: Int) {
- Assert.assertNotNull(wtfHandler)
- Assert.assertEquals(expectedWtfCount, wtfCount)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 36a4712..20020f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -43,6 +43,7 @@
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
@@ -418,13 +419,13 @@
}
@Test
- fun shadeCollpaseFadeIn() =
+ fun shadeCollapseFadeIn() =
testScope.runTest {
+ val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn)
+
// Start on lockscreen without the shade
underTest.setShadeCollapseFadeInComplete(false)
showLockscreen()
-
- val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn)
assertThat(fadeIn).isEqualTo(false)
// ... then the shade expands
@@ -440,10 +441,12 @@
assertThat(fadeIn).isEqualTo(false)
}
- private suspend fun showLockscreen() {
+ private suspend fun TestScope.showLockscreen() {
shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(0f)
+ runCurrent()
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
to = KeyguardState.LOCKSCREEN,
@@ -451,10 +454,12 @@
)
}
- private suspend fun showLockscreenWithShadeExpanded() {
+ private suspend fun TestScope.showLockscreenWithShadeExpanded() {
shadeRepository.setLockscreenShadeExpansion(1f)
shadeRepository.setQsExpansion(0f)
+ runCurrent()
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
to = KeyguardState.LOCKSCREEN,
@@ -462,10 +467,12 @@
)
}
- private suspend fun showLockscreenWithQSExpanded() {
+ private suspend fun TestScope.showLockscreenWithQSExpanded() {
shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(1f)
+ runCurrent()
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index e1bd89f..2b1f5fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
import static junit.framework.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 62d8f7f..9f4e1dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
@@ -73,6 +74,7 @@
@Test
fun calculateWidthFor_fiveIcons_widthForFourIcons() {
+ mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
iconContainer.setIconSize(10)
@@ -151,7 +153,7 @@
iconContainer.addView(iconFive)
assertEquals(5, iconContainer.childCount)
- val width = iconContainer.calculateWidthFor(/* numIcons= */ 5f)
+ val width = iconContainer.calculateWidthFor(/* numIcons= */ 4f)
iconContainer.setActualLayoutWidth(width.toInt())
iconContainer.calculateIconXTranslations()
@@ -212,6 +214,7 @@
@Test
fun shouldForceOverflow_appearingAboveSpeedBump_true() {
+ mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
val forceOverflow =
iconContainer.shouldForceOverflow(
/* i= */ 1,
@@ -228,7 +231,7 @@
iconContainer.shouldForceOverflow(
/* i= */ 10,
/* speedBumpIndex= */ 11,
- /* iconAppearAmount= */ 0f,
+ /* iconAppearAmount= */ 0.1f,
/* maxVisibleIcons= */ 5
)
assertTrue(forceOverflow)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
index 7594c90..feff046 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.phone
import android.graphics.Point
+import android.testing.TestableLooper
import android.view.Display
import android.view.Surface
import android.view.View
@@ -19,6 +20,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@TestableLooper.RunWithLooper
class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 592c78f..597e2e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -20,7 +20,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.google.common.truth.Truth.assertThat;
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 1cc611c..54d3607 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
@@ -36,6 +36,7 @@
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -43,7 +44,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.animation.AnimatorTestRule;
-import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.LogBuffer;
@@ -57,9 +57,7 @@
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
@@ -70,7 +68,6 @@
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
import com.android.systemui.util.CarrierConfigTracker;
@@ -95,7 +92,6 @@
private NotificationIconAreaController mMockNotificationAreaController;
private ShadeExpansionStateManager mShadeExpansionStateManager;
- private View mNotificationAreaInner;
private OngoingCallController mOngoingCallController;
private SystemStatusAnimationScheduler mAnimationScheduler;
private StatusBarLocationPublisher mLocationPublisher;
@@ -274,15 +270,15 @@
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@Test
@@ -314,7 +310,7 @@
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -330,7 +326,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -347,7 +343,7 @@
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the shade is updated to no longer be open
@@ -358,7 +354,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -372,7 +368,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -386,7 +382,7 @@
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -400,7 +396,7 @@
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the transition has finished
@@ -409,7 +405,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -442,7 +438,7 @@
assertEquals(View.VISIBLE,
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@Test
@@ -507,8 +503,8 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
// Notification area is hidden without delay
- assertEquals(0f, mNotificationAreaInner.getAlpha(), 0.01);
- assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
+ assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@Test
@@ -702,7 +698,7 @@
mKeyguardStateController,
mShadeViewController,
mStatusBarStateController,
- mock(StatusBarIconViewBindingFailureTracker.class),
+ mock(NotificationIconContainerStatusBarViewBinder.class),
mCommandQueue,
mCarrierConfigTracker,
new CollapsedStatusBarFragmentLogger(
@@ -715,10 +711,6 @@
mDumpManager,
mStatusBarWindowStateController,
mKeyguardUpdateMonitor,
- mock(NotificationIconContainerStatusBarViewModel.class),
- mock(ConfigurationState.class),
- mock(SystemBarUtilsState.class),
- mock(StatusBarNotificationIconViewStore.class),
mock(DemoModeController.class));
}
@@ -731,11 +723,10 @@
private void setUpNotificationIconAreaController() {
mMockNotificationAreaController = mock(NotificationIconAreaController.class);
-
- mNotificationAreaInner = new View(mContext);
-
- when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
- mNotificationAreaInner);
+ View notificationAreaInner =
+ LayoutInflater.from(mContext).inflate(R.layout.notification_icon_area, null);
+ when(mMockNotificationAreaController.getNotificationInnerAreaView())
+ .thenReturn(notificationAreaInner);
}
/**
@@ -790,4 +781,8 @@
private View getEndSideContentView() {
return mFragment.getView().findViewById(R.id.status_bar_end_side_content);
}
+
+ private View getNotificationAreaView() {
+ return mFragment.getView().findViewById(R.id.notificationIcons);
+ }
}
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 0f779d9..44fa132 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
@@ -16,7 +16,8 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
-import android.platform.test.flag.junit.SetFlagsRule
+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.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
@@ -73,8 +74,6 @@
class MobileIconViewModelTest : SysuiTestCase() {
private var connectivityRepository = FakeConnectivityRepository()
- private val setFlagsRule = SetFlagsRule()
-
private lateinit var underTest: MobileIconViewModel
private lateinit var interactor: MobileIconInteractorImpl
private lateinit var iconsInteractor: MobileIconsInteractorImpl
@@ -561,11 +560,9 @@
}
@Test
+ @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
fun dataActivity_configOn_testIndicators_staticFlagOff() =
testScope.runTest {
- // GIVEN STATUS_BAR_STATIC_NETWORK_INDICATORS flag is off
- setFlagsRule.disableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
-
// Create a new view model here so the constants are properly read
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
@@ -618,11 +615,9 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
fun dataActivity_configOn_testIndicators_staticFlagOn() =
testScope.runTest {
- // GIVEN STATUS_BAR_STATIC_NETWORK_INDICATORS flag is on
- setFlagsRule.enableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
-
// Create a new view model here so the constants are properly read
whenever(constants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 4c893e3..6a3b2c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -18,7 +18,7 @@
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
import static com.google.common.truth.Truth.assertThat;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index bfc5bdb..0581e0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -18,7 +18,7 @@
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
-import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
import static com.google.common.truth.Truth.assertThat;
@@ -62,10 +62,10 @@
import androidx.test.filters.SmallTest;
import com.android.internal.util.IntPair;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
index e461e3f..bbc96f70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
@@ -26,8 +26,11 @@
private val recordings: MutableList<UnfoldTransitionRecording> = arrayListOf()
private var currentRecording: UnfoldTransitionRecording? = null
+ var lastCallbackThread: Thread? = null
+ private set
override fun onTransitionStarted() {
+ lastCallbackThread = Thread.currentThread()
assertWithMessage("Trying to start a transition when it is already in progress")
.that(currentRecording)
.isNull()
@@ -36,6 +39,7 @@
}
override fun onTransitionProgress(progress: Float) {
+ lastCallbackThread = Thread.currentThread()
assertWithMessage("Received transition progress event when it's not started")
.that(currentRecording)
.isNotNull()
@@ -43,6 +47,7 @@
}
override fun onTransitionFinishing() {
+ lastCallbackThread = Thread.currentThread()
assertWithMessage("Received transition finishing event when it's not started")
.that(currentRecording)
.isNotNull()
@@ -50,6 +55,7 @@
}
override fun onTransitionFinished() {
+ lastCallbackThread = Thread.currentThread()
assertWithMessage("Received transition finish event when it's not started")
.that(currentRecording)
.isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index a25469b..d864d53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.unfold.util
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import android.view.Surface
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -37,6 +38,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@TestableLooper.RunWithLooper
class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
@Mock lateinit var rotationChangeProvider: RotationChangeProvider
@@ -48,10 +50,12 @@
@Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener>
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+ private lateinit var testableLooper : TestableLooper
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
progressProvider =
NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, sourceProvider)
@@ -123,5 +127,6 @@
private fun onRotationChanged(rotation: Int) {
rotationListenerCaptor.value.onRotationChanged(rotation)
+ testableLooper.processAllMessages()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index e1e54a9..2f29b3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -19,6 +19,7 @@
import android.database.ContentObserver
import android.provider.Settings
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.TestUnfoldTransitionProvider
@@ -36,6 +37,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@TestableLooper.RunWithLooper
class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
@Mock lateinit var sinkProvider: TransitionProgressListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
new file mode 100644
index 0000000..5b4f4d3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
@@ -0,0 +1,161 @@
+/*
+ * 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.unfold.util
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Process
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.progress.TestUnfoldProgressListener
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.withTimeout
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class ScopedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
+
+ private val rootProvider = TestUnfoldTransitionProvider()
+ private val listener = TestUnfoldProgressListener()
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+ private val bgThread =
+ HandlerThread("UnfoldBgTest", Process.THREAD_PRIORITY_FOREGROUND).apply { start() }
+ private val bgHandler = Handler(bgThread.looper)
+ private val scopedProvider =
+ ScopedUnfoldTransitionProgressProvider(rootProvider).apply { addCallback(listener) }
+
+ @Test
+ fun setReadyToHandleTransition_whileTransitionRunning_propagatesCallbacks() =
+ testScope.runTest {
+ runBlockingInBg { rootProvider.onTransitionStarted() }
+
+ scopedProvider.setReadyToHandleTransition(true)
+
+ runBlockingInBg { /* sync barrier */}
+
+ listener.assertStarted()
+
+ runBlockingInBg { rootProvider.onTransitionProgress(1f) }
+
+ listener.assertLastProgress(1f)
+
+ runBlockingInBg { rootProvider.onTransitionFinished() }
+
+ listener.assertNotStarted()
+ }
+
+ @Test
+ fun setReadyToHandleTransition_whileTransitionNotRunning_callbacksInProgressThread() {
+ testScope.runTest {
+ scopedProvider.setReadyToHandleTransition(true)
+
+ val bgThread = runBlockingInBg { Thread.currentThread() }
+
+ runBlockingInBg { rootProvider.onTransitionStarted() }
+
+ listener.assertStarted()
+
+ assertThat(listener.lastCallbackThread).isEqualTo(bgThread)
+ }
+ }
+
+ @Test
+ fun setReadyToHandleTransition_beforeAnyCallback_doesNotCrash() {
+ testScope.runTest { scopedProvider.setReadyToHandleTransition(true) }
+ }
+
+ @Test
+ fun onTransitionStarted_whileNotReadyToHandleTransition_doesNotPropagate() {
+ testScope.runTest {
+ scopedProvider.setReadyToHandleTransition(false)
+
+ rootProvider.onTransitionStarted()
+
+ listener.assertNotStarted()
+ }
+ }
+
+ @Test
+ fun onTransitionStarted_defaultReadiness_doesNotPropagate() {
+ testScope.runTest {
+ rootProvider.onTransitionStarted()
+
+ listener.assertNotStarted()
+ }
+ }
+
+ @Test
+ fun onTransitionStarted_fromDifferentThreads_throws() {
+ testScope.runTest {
+ runBlockingInBg {
+ rootProvider.onTransitionStarted()
+ rootProvider.onTransitionFinished()
+ }
+ assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionStarted() }
+ }
+ }
+
+ @Test
+ fun onTransitionProgress_fromDifferentThreads_throws() {
+ testScope.runTest {
+ runBlockingInBg { rootProvider.onTransitionStarted() }
+ assertThrows(IllegalStateException::class.java) {
+ rootProvider.onTransitionProgress(1f)
+ }
+ }
+ }
+
+ @Test
+ fun onTransitionFinished_fromDifferentThreads_throws() {
+ testScope.runTest {
+ runBlockingInBg { rootProvider.onTransitionStarted() }
+ assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionFinished() }
+ }
+ }
+
+ @Test
+ fun onTransitionFinishing_fromDifferentThreads_throws() {
+ testScope.runTest {
+ runBlockingInBg { rootProvider.onTransitionStarted() }
+ assertThrows(IllegalStateException::class.java) { rootProvider.onTransitionFinishing() }
+ }
+ }
+
+ private fun <T> runBlockingInBg(f: () -> T): T {
+ return runBlocking {
+ withTimeout(5.seconds) {
+ suspendCancellableCoroutine { c: CancellableContinuation<T> ->
+ bgHandler.post { c.resumeWith(Result.success(f())) }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
index 4a38fc0..f484ea0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.unfold.util
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.TestUnfoldTransitionProvider
@@ -27,6 +28,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@TestableLooper.RunWithLooper
class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
private val listener = TestUnfoldProgressListener()
@@ -54,9 +56,7 @@
sourceProvider.onTransitionProgress(0.5f)
sourceProvider.onTransitionFinished()
- with(listener.ensureTransitionFinished()) {
- assertLastProgress(0.5f)
- }
+ with(listener.ensureTransitionFinished()) { assertLastProgress(0.5f) }
}
@Test
@@ -121,8 +121,6 @@
sourceProvider.onTransitionProgress(0.1f)
sourceProvider.onTransitionFinished()
- with(listener.ensureTransitionFinished()) {
- assertLastProgress(0.1f)
- }
+ with(listener.ensureTransitionFinished()) { assertLastProgress(0.1f) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index bf851eb..6714c94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -1115,6 +1115,7 @@
broadcastDispatcher = fakeBroadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
backgroundDispatcher = utils.testDispatcher,
+ mainDispatcher = utils.testDispatcher,
activityManager = activityManager,
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index d1870b1..21d4549 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -258,6 +258,7 @@
broadcastDispatcher = fakeBroadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
backgroundDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
activityManager = activityManager,
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index b7b24f6..d0804be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -170,6 +170,7 @@
broadcastDispatcher = fakeBroadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
backgroundDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
activityManager = activityManager,
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
deleted file mode 100644
index a2b016f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.util.leak;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.concurrency.MessageRouterImpl;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class GarbageMonitorTest extends SysuiTestCase {
-
- @Mock private LeakReporter mLeakReporter;
- @Mock private TrackedGarbage mTrackedGarbage;
- @Mock private DumpManager mDumpManager;
- private GarbageMonitor mGarbageMonitor;
- private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mGarbageMonitor =
- new GarbageMonitor(
- mContext,
- mFakeExecutor,
- new MessageRouterImpl(mFakeExecutor),
- new LeakDetector(null, mTrackedGarbage, null, mDumpManager),
- mLeakReporter,
- mDumpManager);
- }
-
- @Test
- public void testALittleGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE);
-
- mGarbageMonitor.reinspectGarbageAfterGc();
-
- verify(mLeakReporter, never()).dumpLeak(anyInt());
- }
-
- @Test
- public void testTransientGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
-
- // Start the leak monitor. Nothing gets reported immediately.
- mGarbageMonitor.startLeakMonitor();
- mFakeExecutor.runAllReady();
- verify(mLeakReporter, never()).dumpLeak(anyInt());
-
- // Garbage gets reset to 0 before the leak reporte actually gets called.
- when(mTrackedGarbage.countOldGarbage()).thenReturn(0);
- mFakeExecutor.advanceClockToLast();
- mFakeExecutor.runAllReady();
-
- // Therefore nothing gets dumped.
- verify(mLeakReporter, never()).dumpLeak(anyInt());
- }
-
- @Test
- public void testLotsOfPersistentGarbage_dumps() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
-
- mGarbageMonitor.reinspectGarbageAfterGc();
-
- verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
- }
-
- @Test
- public void testLotsOfPersistentGarbage_dumpsAfterAtime() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
-
- // Start the leak monitor. Nothing gets reported immediately.
- mGarbageMonitor.startLeakMonitor();
- mFakeExecutor.runAllReady();
- verify(mLeakReporter, never()).dumpLeak(anyInt());
-
- mFakeExecutor.advanceClockToLast();
- mFakeExecutor.runAllReady();
-
- verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
index e59e475..7801684 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -28,8 +28,8 @@
import com.android.systemui.model.SysUiStateTest
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleEducationController
-import com.android.wm.shell.bubbles.PREF_MANAGED_EDUCATION
-import com.android.wm.shell.bubbles.PREF_STACK_EDUCATION
+import com.android.wm.shell.bubbles.ManageEducationView.Companion.PREF_MANAGED_EDUCATION
+import com.android.wm.shell.bubbles.StackEducationView.Companion.PREF_STACK_EDUCATION
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Assert.assertEquals
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 b217195..814ea19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -29,8 +29,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -50,6 +48,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
@@ -186,7 +186,7 @@
import com.android.wm.shell.bubbles.BubbleViewInfoTask;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.bubbles.StackEducationViewKt;
+import com.android.wm.shell.bubbles.StackEducationView;
import com.android.wm.shell.bubbles.properties.BubbleProperties;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -1930,7 +1930,7 @@
@Test
public void testShowStackEdu_isNotConversationBubble() {
// Setup
- setPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION, false);
+ setPrefBoolean(StackEducationView.PREF_STACK_EDUCATION, false);
BubbleEntry bubbleEntry = createBubbleEntry(false /* isConversation */);
mBubbleController.updateBubble(bubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -1948,7 +1948,7 @@
@Test
public void testShowStackEdu_isConversationBubble() {
// Setup
- setPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION, false);
+ setPrefBoolean(StackEducationView.PREF_STACK_EDUCATION, false);
BubbleEntry bubbleEntry = createBubbleEntry(true /* isConversation */);
mBubbleController.updateBubble(bubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -1966,7 +1966,7 @@
@Test
public void testShowStackEdu_isSeenConversationBubble() {
// Setup
- setPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION, true);
+ setPrefBoolean(StackEducationView.PREF_STACK_EDUCATION, true);
BubbleEntry bubbleEntry = createBubbleEntry(true /* isConversation */);
mBubbleController.updateBubble(bubbleEntry);
assertTrue(mBubbleController.hasBubbles());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
index de310b4..e24ba26 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
@@ -50,7 +50,9 @@
@Module
interface Bindings {
+ @Binds @Main fun bindMainContext(dispatcher: TestDispatcher): CoroutineContext
@Binds @Main fun bindMainDispatcher(dispatcher: TestDispatcher): CoroutineDispatcher
+ @Binds @Background fun bindBgContext(dispatcher: TestDispatcher): CoroutineContext
@Binds @Background fun bindBgDispatcher(dispatcher: TestDispatcher): CoroutineDispatcher
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryKosmos.kt
index a464fa8..fa79580 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryKosmos.kt
@@ -16,10 +16,8 @@
package com.android.systemui.accessibility.data.repository
-import android.view.accessibility.accessibilityManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-val Kosmos.accessibilityRepository by Fixture {
- AccessibilityRepository.invoke(a11yManager = accessibilityManager)
-}
+val Kosmos.fakeAccessibilityRepository by Fixture { FakeAccessibilityRepository() }
+val Kosmos.accessibilityRepository by Fixture { fakeAccessibilityRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 7c5696c..a6dd3cd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -16,11 +16,11 @@
package com.android.systemui.authentication.data.repository
+import android.os.UserHandle
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
@@ -29,7 +29,6 @@
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -40,15 +39,11 @@
private val currentTime: () -> Long,
) : AuthenticationRepository {
- override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
-
override val hintedPinLength: Int = HINTING_PIN_LENGTH
private val _isPatternVisible = MutableStateFlow(true)
override val isPatternVisible: StateFlow<Boolean> = _isPatternVisible.asStateFlow()
- override val lockout: MutableStateFlow<AuthenticationLockoutModel?> = MutableStateFlow(null)
-
override val hasLockoutOccurred = MutableStateFlow(false)
private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
@@ -68,8 +63,6 @@
override val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> =
_isPinEnhancedPrivacyEnabled.asStateFlow()
- private var failedAttemptCount = 0
- private var lockoutEndTimestamp = 0L
private var credentialOverride: List<Any>? = null
private var securityMode: SecurityMode = DEFAULT_AUTHENTICATION_METHOD.toSecurityMode()
@@ -89,37 +82,45 @@
}
override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
- failedAttemptCount = if (isSuccessful) 0 else failedAttemptCount + 1
- authenticationChallengeResult.emit(isSuccessful)
+ if (isSuccessful) {
+ _failedAuthenticationAttempts.value = 0
+ _lockoutEndTimestamp = null
+ hasLockoutOccurred.value = false
+ lockoutStartedReportCount = 0
+ } else {
+ _failedAuthenticationAttempts.value++
+ }
}
+ private var _failedAuthenticationAttempts = MutableStateFlow(0)
+ override val failedAuthenticationAttempts: StateFlow<Int> =
+ _failedAuthenticationAttempts.asStateFlow()
+
+ private var _lockoutEndTimestamp: Long? = null
+ override val lockoutEndTimestamp: Long?
+ get() = if (currentTime() < (_lockoutEndTimestamp ?: 0)) _lockoutEndTimestamp else null
+
override suspend fun reportLockoutStarted(durationMs: Int) {
+ _lockoutEndTimestamp = (currentTime() + durationMs).takeIf { durationMs > 0 }
+ hasLockoutOccurred.value = true
lockoutStartedReportCount++
}
+ override suspend fun getMaxFailedUnlockAttemptsForWipe(): Int =
+ MAX_FAILED_AUTH_TRIES_BEFORE_WIPE
+
+ var profileWithMinFailedUnlockAttemptsForWipe: Int = UserHandle.USER_SYSTEM
+ override suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int =
+ profileWithMinFailedUnlockAttemptsForWipe
+
override suspend fun getPinLength(): Int {
return (credentialOverride ?: DEFAULT_PIN).size
}
- override suspend fun getFailedAuthenticationAttemptCount(): Int {
- return failedAttemptCount
- }
-
- override suspend fun getLockoutEndTimestamp(): Long {
- return lockoutEndTimestamp
- }
-
fun setAutoConfirmFeatureEnabled(isEnabled: Boolean) {
_isAutoConfirmFeatureEnabled.value = isEnabled
}
- override suspend fun setLockoutDuration(durationMs: Int) {
- lockoutEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0
- if (durationMs > 0) {
- hasLockoutOccurred.value = true
- }
- }
-
override suspend fun checkCredential(
credential: LockscreenCredential
): AuthenticationResultModel {
@@ -136,8 +137,8 @@
else -> error("Unexpected credential type ${credential.type}!")
}
- return if (isSuccessful || failedAttemptCount < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
- hasLockoutOccurred.value = false
+ val failedAttempts = _failedAuthenticationAttempts.value
+ return if (isSuccessful || failedAttempts < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) {
AuthenticationResultModel(
isSuccessful = isSuccessful,
lockoutDurationMs = 0,
@@ -176,6 +177,9 @@
AuthenticationPatternCoordinate(0, 2),
)
const val MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT = 5
+ const val MAX_FAILED_AUTH_TRIES_BEFORE_WIPE =
+ MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT +
+ LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE
const val LOCKOUT_DURATION_SECONDS = 30
const val LOCKOUT_DURATION_MS = LOCKOUT_DURATION_SECONDS * 1000
const val HINTING_PIN_LENGTH = 6
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
index 060ca4c..7c8a7c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
@@ -19,17 +19,13 @@
import com.android.systemui.authentication.data.repository.authenticationRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.user.data.repository.userRepository
-import com.android.systemui.util.time.fakeSystemClock
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
val Kosmos.authenticationInteractor by
Kosmos.Fixture {
AuthenticationInteractor(
applicationScope = applicationCoroutineScope,
repository = authenticationRepository,
- backgroundDispatcher = testDispatcher,
- userRepository = userRepository,
- clock = fakeSystemClock,
+ selectedUserInteractor = selectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/AuthControllerKosmos.kt
similarity index 71%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/AuthControllerKosmos.kt
index f7f16a4..7f70785 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/AuthControllerKosmos.kt
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.icon.ui.viewbinder
+package com.android.systemui.biometrics
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.stack.ui.viewbinder.notifCollection
+import com.android.systemui.util.mockito.mock
-val Kosmos.shelfNotificationIconViewStore by Fixture {
- ShelfNotificationIconViewStore(notifCollection = notifCollection)
-}
+var Kosmos.authController by Fixture { mock<AuthController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
new file mode 100644
index 0000000..cbfc768
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.content.applicationContext
+import com.android.systemui.biometrics.authController
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.udfpsOverlayInteractor by Fixture {
+ UdfpsOverlayInteractor(
+ context = applicationContext,
+ authController = authController,
+ selectedUserInteractor = selectedUserInteractor,
+ scope = applicationCoroutineScope,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/ui/viewmodel/UdfpsAccessibilityOverlayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/ui/viewmodel/UdfpsAccessibilityOverlayViewModelKosmos.kt
new file mode 100644
index 0000000..cdeade1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/ui/viewmodel/UdfpsAccessibilityOverlayViewModelKosmos.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.deviceentry.data.ui.viewmodel
+
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
+import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
+import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.viewmodel.deviceEntryForegroundIconViewModel
+import com.android.systemui.keyguard.ui.viewmodel.deviceEntryIconViewModel
+import com.android.systemui.kosmos.Kosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel by
+ Kosmos.Fixture {
+ DeviceEntryUdfpsAccessibilityOverlayViewModel(
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ accessibilityInteractor = accessibilityInteractor,
+ deviceEntryIconViewModel = deviceEntryIconViewModel,
+ deviceEntryFgIconViewModel = deviceEntryForegroundIconViewModel,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryFgIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryFgIconViewModelKosmos.kt
new file mode 100644
index 0000000..4bfe4f5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryFgIconViewModelKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.deviceEntryForegroundIconViewModel by Fixture {
+ DeviceEntryForegroundViewModel(
+ context = applicationContext,
+ configurationRepository = configurationRepository,
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ transitionInteractor = keyguardTransitionInteractor,
+ deviceEntryIconViewModel = deviceEntryIconViewModel,
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
index 67e9289..5ceefde 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
@@ -28,8 +28,10 @@
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
+val Kosmos.fakeDeviceEntryIconViewModelTransition by Fixture { FakeDeviceEntryIconTransition() }
+
val Kosmos.deviceEntryIconViewModelTransitionsMock by Fixture {
- mutableSetOf<DeviceEntryIconTransition>()
+ setOf<DeviceEntryIconTransition>(fakeDeviceEntryIconViewModelTransition)
}
val Kosmos.deviceEntryIconViewModel by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/FakeDeviceEntryIconTransition.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/FakeDeviceEntryIconTransition.kt
new file mode 100644
index 0000000..6d872a3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/FakeDeviceEntryIconTransition.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeDeviceEntryIconTransition : DeviceEntryIconTransition {
+ private val _deviceEntryParentViewAlpha: MutableStateFlow<Float> = MutableStateFlow(0f)
+ override val deviceEntryParentViewAlpha: Flow<Float> = _deviceEntryParentViewAlpha.asStateFlow()
+
+ fun setDeviceEntryParentViewAlpha(alpha: Float) {
+ _deviceEntryParentViewAlpha.value = alpha
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
index 6ccb3bc..5e67182 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
@@ -20,6 +20,29 @@
import android.util.Log.TerribleFailureHandler
import junit.framework.Assert
+/** Asserts that the given block does not make a call to Log.wtf */
+fun assertDoesNotLogWtf(
+ message: String = "Expected Log.wtf not to be called",
+ notLoggingBlock: () -> Unit,
+) {
+ var caught: TerribleFailureLog? = null
+ val newHandler = TerribleFailureHandler { tag, failure, system ->
+ caught = TerribleFailureLog(tag, failure, system)
+ }
+ val oldHandler = Log.setWtfHandler(newHandler)
+ try {
+ notLoggingBlock()
+ } finally {
+ Log.setWtfHandler(oldHandler)
+ }
+ caught?.let { throw AssertionError("$message: $it", it.failure) }
+}
+
+fun assertDoesNotLogWtf(
+ message: String = "Expected Log.wtf not to be called",
+ notLoggingRunnable: Runnable,
+) = assertDoesNotLogWtf(message = message) { notLoggingRunnable.run() }
+
/**
* Assert that the given block makes a call to Log.wtf
*
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogBufferHelper.kt
similarity index 72%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/log/LogBufferHelper.kt
index 0538227..45ecb4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogBufferHelper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,23 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.dump
+package com.android.systemui.log
-import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.LogcatEchoTracker
-/**
- * Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests.
- */
+/** Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests. */
@JvmOverloads
fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") =
LogBuffer(name, 50, LogcatEchoTrackerAlways())
-/**
- * A [LogcatEchoTracker] that always allows echoing to the logcat.
- */
+/** A [LogcatEchoTracker] that always allows echoing to the logcat. */
class LogcatEchoTrackerAlways : LogcatEchoTracker {
override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = true
+
override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true
}
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessKosmos.kt
similarity index 74%
rename from core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
rename to packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessKosmos.kt
index 6c1f0fc..79167f8 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessKosmos.kt
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-package android.companion.virtual.camera;
+package com.android.systemui.process
-/**
- * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
- * VirtualCamera.
- * @hide
- */
-parcelable VirtualCameraMetadata;
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.processWrapper: ProcessWrapperFake by Kosmos.Fixture { ProcessWrapperFake() }
diff --git a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
similarity index 74%
copy from core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
index 6c1f0fc..9841778 100644
--- a/core/java/android/companion/virtual/camera/VirtualCameraMetadata.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/process/ProcessWrapperFake.kt
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package android.companion.virtual.camera;
+package com.android.systemui.process
-/**
- * Data structure used to store {@link android.hardware.camera2.CameraMetadata} compatible with
- * VirtualCamera.
- * @hide
- */
-parcelable VirtualCameraMetadata;
+class ProcessWrapperFake : ProcessWrapper() {
+
+ var systemUser: Boolean = false
+
+ override fun isSystemUser(): Boolean {
+ return systemUser
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 0b41926..9f71161 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene
import android.app.ActivityTaskManager
+import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.Intent
import android.content.pm.UserInfo
@@ -91,12 +92,12 @@
import com.android.systemui.telephony.data.repository.TelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserViewModel
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.SystemClock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
@@ -104,7 +105,6 @@
import kotlinx.coroutines.test.currentTime
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mockito
/**
* Utilities for creating scene container framework related repositories, interactors, and
@@ -145,18 +145,20 @@
)
}
val telephonyRepository: FakeTelephonyRepository by lazy { FakeTelephonyRepository() }
-
val bouncerRepository = BouncerRepository(featureFlags)
val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
val keyguardRepository: FakeKeyguardRepository by lazy { FakeKeyguardRepository() }
val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
val simBouncerRepository: FakeSimBouncerRepository by lazy { FakeSimBouncerRepository() }
- val telephonyManager: TelephonyManager =
- Mockito.mock(TelephonyManager::class.java).apply {
- whenever(createForSubscriptionId(anyInt())).thenReturn(this)
- whenever(supplyIccLockPin(anyString()))
- .thenReturn(PinResult(PIN_RESULT_TYPE_SUCCESS, 3))
- }
+
+ val clock: SystemClock = mock {
+ whenever(elapsedRealtime()).thenAnswer { testScope.currentTime }
+ }
+ val telephonyManager: TelephonyManager = mock {
+ whenever(createForSubscriptionId(anyInt())).thenReturn(this)
+ whenever(supplyIccLockPin(anyString())).thenReturn(PinResult(PIN_RESULT_TYPE_SUCCESS, 3))
+ }
+ val devicePolicyManager: DevicePolicyManager = mock {}
val mobileConnectionsRepository: FakeMobileConnectionsRepository by lazy {
FakeMobileConnectionsRepository(mock(), mock())
}
@@ -174,7 +176,7 @@
mobileConnectionsRepository = mobileConnectionsRepository,
)
- val userRepository: UserRepository by lazy {
+ val userRepository: FakeUserRepository by lazy {
FakeUserRepository().apply {
val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0))
setUserInfos(users)
@@ -236,9 +238,7 @@
return AuthenticationInteractor(
applicationScope = applicationScope(),
repository = repository,
- backgroundDispatcher = testDispatcher,
- userRepository = userRepository,
- clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } }
+ selectedUserInteractor = selectedUserInteractor(),
)
}
@@ -274,7 +274,6 @@
repository = bouncerRepository,
authenticationInteractor = authenticationInteractor,
keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
- flags = sceneContainerFlags,
falsingInteractor = falsingInteractor(),
powerInteractor = powerInteractor(),
simBouncerInteractor = simBouncerInteractor,
@@ -297,7 +296,7 @@
fun bouncerViewModel(
bouncerInteractor: BouncerInteractor,
authenticationInteractor: AuthenticationInteractor,
- actionButtonInteractor: BouncerActionButtonInteractor,
+ actionButtonInteractor: BouncerActionButtonInteractor = bouncerActionButtonInteractor(),
users: List<UserViewModel> = createUsers(),
): BouncerViewModel {
return BouncerViewModel(
@@ -305,13 +304,15 @@
applicationScope = applicationScope(),
mainDispatcher = testDispatcher,
bouncerInteractor = bouncerInteractor,
+ simBouncerInteractor = simBouncerInteractor,
authenticationInteractor = authenticationInteractor,
flags = sceneContainerFlags,
selectedUser = flowOf(users.first { it.isSelectionMarkerVisible }),
users = flowOf(users),
userSwitcherMenu = flowOf(createMenuActions()),
- actionButtonInteractor = actionButtonInteractor,
- simBouncerInteractor = simBouncerInteractor,
+ actionButton = actionButtonInteractor.actionButton,
+ clock = clock,
+ devicePolicyManager = devicePolicyManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
index 5c8fe0d..774782c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt
@@ -21,28 +21,35 @@
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.kosmos.testDispatcher
import com.android.systemui.statusbar.data.repository.notificationListenerSettingsRepository
import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.wm.shell.bubbles.bubblesOptional
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.alwaysOnDisplayNotificationIconsInteractor by Fixture {
AlwaysOnDisplayNotificationIconsInteractor(
+ bgContext = testDispatcher,
deviceEntryInteractor = deviceEntryInteractor,
iconsInteractor = notificationIconsInteractor,
)
}
+
val Kosmos.statusBarNotificationIconsInteractor by Fixture {
StatusBarNotificationIconsInteractor(
+ bgContext = testDispatcher,
iconsInteractor = notificationIconsInteractor,
settingsRepository = notificationListenerSettingsRepository,
)
}
+
val Kosmos.notificationIconsInteractor by Fixture {
NotificationIconsInteractor(
activeNotificationsInteractor = activeNotificationsInteractor,
bubbles = bubblesOptional,
+ headsUpNotificationIconInteractor = headsUpNotificationIconInteractor,
keyguardViewStateRepository = notificationsKeyguardViewStateRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
similarity index 64%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
index f7f16a4..67fecb4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
@@ -16,9 +16,22 @@
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
+import com.android.systemui.common.ui.configurationState
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.notifCollection
+import com.android.systemui.statusbar.ui.systemBarUtilsState
+
+val Kosmos.notificationIconContainerShelfViewBinder by Fixture {
+ NotificationIconContainerShelfViewBinder(
+ notificationIconContainerShelfViewModel,
+ configurationState,
+ systemBarUtilsState,
+ statusBarIconViewBindingFailureTracker,
+ shelfNotificationIconViewStore,
+ )
+}
val Kosmos.shelfNotificationIconViewStore by Fixture {
ShelfNotificationIconViewStore(notifCollection = notifCollection)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
index 6295b83..18c063f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt
@@ -20,12 +20,14 @@
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.alwaysOnDisplayNotificationIconsInteractor
val Kosmos.notificationIconContainerAlwaysOnDisplayViewModel by
Kosmos.Fixture {
NotificationIconContainerAlwaysOnDisplayViewModel(
+ bgContext = testDispatcher,
iconsInteractor = alwaysOnDisplayNotificationIconsInteractor,
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt
index d679bb6..4492af5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModelKosmos.kt
@@ -17,11 +17,13 @@
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.statusbar.notification.icon.domain.interactor.notificationIconsInteractor
val Kosmos.notificationIconContainerShelfViewModel by
Kosmos.Fixture {
NotificationIconContainerShelfViewModel(
+ bgContext = testDispatcher,
interactor = notificationIconsInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
index 04bb52d..3632a3d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt
@@ -19,8 +19,8 @@
import android.content.res.mainResources
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.statusBarNotificationIconsInteractor
import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor
@@ -28,11 +28,11 @@
val Kosmos.notificationIconContainerStatusBarViewModel by
Kosmos.Fixture {
NotificationIconContainerStatusBarViewModel(
+ bgContext = testDispatcher,
darkIconInteractor = darkIconInteractor,
iconsInteractor = statusBarNotificationIconsInteractor,
headsUpIconInteractor = headsUpNotificationIconInteractor,
keyguardInteractor = keyguardInteractor,
- notificationsInteractor = activeNotificationsInteractor,
resources = mainResources,
shadeInteractor = shadeInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
index 988172c..b906b60 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
@@ -18,7 +18,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.row.ui.viewmodel.activatableNotificationViewModel
import com.android.systemui.statusbar.notification.shelf.domain.interactor.notificationShelfInteractor
@@ -26,6 +25,5 @@
NotificationShelfViewModel(
interactor = notificationShelfInteractor,
activatableViewModel = activatableNotificationViewModel,
- icons = notificationIconContainerShelfViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
index ca5b401..04716b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
@@ -22,11 +22,9 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.shelfNotificationIconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.statusBarIconViewBindingFailureTracker
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.notificationIconContainerShelfViewBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationListViewModel
import com.android.systemui.statusbar.phone.notificationIconAreaController
-import com.android.systemui.statusbar.ui.systemBarUtilsState
val Kosmos.notificationListViewBinder by Fixture {
NotificationListViewBinder(
@@ -35,9 +33,7 @@
configuration = configurationState,
falsingManager = falsingManager,
iconAreaController = notificationIconAreaController,
- iconViewBindingFailureTracker = statusBarIconViewBindingFailureTracker,
metricsLogger = metricsLogger,
- shelfIconViewStore = shelfNotificationIconViewStore,
- systemBarUtilsState = systemBarUtilsState,
+ nicBinder = notificationIconContainerShelfViewBinder,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
index 42c77aa..4e2dc7a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
@@ -47,6 +47,7 @@
broadcastDispatcher = broadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
backgroundDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
activityManager = activityManager,
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 81fd8ce..e52cefb 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -39,4 +39,7 @@
sdk_version: "current",
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
index f9751d9..2bca272 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
@@ -15,8 +15,11 @@
*/
package com.android.systemui.unfold.util
+import android.os.Handler
+import android.os.Looper
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import java.util.concurrent.CopyOnWriteArrayList
/**
* Manages progress listeners that can have smaller lifespan than the unfold animation.
@@ -33,12 +36,13 @@
constructor(source: UnfoldTransitionProgressProvider? = null) :
UnfoldTransitionProgressProvider, TransitionProgressListener {
+ private var progressHandler: Handler? = null
private var source: UnfoldTransitionProgressProvider? = null
- private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+ private val listeners = CopyOnWriteArrayList<TransitionProgressListener>()
- private var isReadyToHandleTransition = false
- private var isTransitionRunning = false
+ @Volatile private var isReadyToHandleTransition = false
+ @Volatile private var isTransitionRunning = false
private var lastTransitionProgress = PROGRESS_UNSET
init {
@@ -70,15 +74,18 @@
* Call it with readyToHandleTransition = false when listeners can't process the events.
*/
fun setReadyToHandleTransition(isReadyToHandleTransition: Boolean) {
- if (isTransitionRunning) {
- if (isReadyToHandleTransition) {
- listeners.forEach { it.onTransitionStarted() }
- if (lastTransitionProgress != PROGRESS_UNSET) {
- listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
+ val progressHandler = this.progressHandler
+ if (isTransitionRunning && progressHandler != null) {
+ progressHandler.post {
+ if (isReadyToHandleTransition) {
+ listeners.forEach { it.onTransitionStarted() }
+ if (lastTransitionProgress != PROGRESS_UNSET) {
+ listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
+ }
+ } else {
+ isTransitionRunning = false
+ listeners.forEach { it.onTransitionFinished() }
}
- } else {
- isTransitionRunning = false
- listeners.forEach { it.onTransitionFinished() }
}
}
this.isReadyToHandleTransition = isReadyToHandleTransition
@@ -98,6 +105,7 @@
}
override fun onTransitionStarted() {
+ assertInProgressThread()
isTransitionRunning = true
if (isReadyToHandleTransition) {
listeners.forEach { it.onTransitionStarted() }
@@ -105,6 +113,7 @@
}
override fun onTransitionProgress(progress: Float) {
+ assertInProgressThread()
if (isReadyToHandleTransition) {
listeners.forEach { it.onTransitionProgress(progress) }
}
@@ -112,12 +121,14 @@
}
override fun onTransitionFinishing() {
+ assertInProgressThread()
if (isReadyToHandleTransition) {
listeners.forEach { it.onTransitionFinishing() }
}
}
override fun onTransitionFinished() {
+ assertInProgressThread()
if (isReadyToHandleTransition) {
listeners.forEach { it.onTransitionFinished() }
}
@@ -125,6 +136,21 @@
lastTransitionProgress = PROGRESS_UNSET
}
+ private fun assertInProgressThread() {
+ val cachedProgressHandler = progressHandler
+ if (cachedProgressHandler == null) {
+ val thisLooper = Looper.myLooper() ?: error("This thread is expected to have a looper.")
+ progressHandler = Handler(thisLooper)
+ } else {
+ check(cachedProgressHandler.looper.isCurrentThread) {
+ """Receiving unfold transition callback from different threads.
+ |Current: ${Thread.currentThread()}
+ |expected: ${cachedProgressHandler.looper.thread}"""
+ .trimMargin()
+ }
+ }
+ }
+
companion object {
private const val PROGRESS_UNSET = -1f
}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 7744fca..491ed22 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -55,6 +55,7 @@
android.os.Parcel
android.os.Parcelable
android.os.Process
+android.os.ServiceSpecificException
android.os.SystemClock
android.os.ThreadLocalWorkSource
android.os.TimestampedValue
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a4b2896..cab2d74 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.removeAppWidgetServiceIoFromCriticalPath;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -144,6 +145,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -277,7 +279,12 @@
mKeyguardManager = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mSaveStateHandler = BackgroundThread.getHandler();
+ if (removeAppWidgetServiceIoFromCriticalPath()) {
+ mSaveStateHandler = new Handler(BackgroundThread.get().getLooper(),
+ this::handleSaveMessage);
+ } else {
+ mSaveStateHandler = BackgroundThread.getHandler();
+ }
final ServiceThread serviceThread = new ServiceThread(TAG,
android.os.Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
serviceThread.start();
@@ -314,6 +321,40 @@
mMaxWidgetBitmapMemory = 6 * size.x * size.y;
}
+ private boolean handleSaveMessage(Message msg) {
+ final int userId = msg.what;
+ SparseArray<byte[]> userIdToBytesMapping;
+ synchronized (mLock) {
+ // No need to enforce unlocked state when there is no caller. User can be in the
+ // stopping state or removed by the time the message is processed
+ ensureGroupStateLoadedLocked(userId, false /* enforceUserUnlockingOrUnlocked */);
+ userIdToBytesMapping = saveStateToByteArrayLocked(userId);
+ }
+
+ for (int i = 0; i < userIdToBytesMapping.size(); i++) {
+ int currentProfileId = userIdToBytesMapping.keyAt(i);
+ byte[] currentStateByteArray = userIdToBytesMapping.valueAt(i);
+ AtomicFile currentFile = getSavedStateFile(currentProfileId);
+ FileOutputStream fileStream;
+ try {
+ fileStream = currentFile.startWrite();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to start writing stream", e);
+ continue;
+ }
+
+ try {
+ fileStream.write(currentStateByteArray);
+ currentFile.finishWrite(fileStream);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write state byte stream to file", e);
+ currentFile.failWrite(fileStream);
+ }
+ }
+
+ return true;
+ }
+
private void registerBroadcastReceiver() {
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -1944,7 +1985,12 @@
}
private void saveGroupStateAsync(int groupId) {
- mSaveStateHandler.post(new SaveStateRunnable(groupId));
+ if (removeAppWidgetServiceIoFromCriticalPath()) {
+ mSaveStateHandler.removeMessages(groupId);
+ mSaveStateHandler.sendEmptyMessage(groupId);
+ } else {
+ mSaveStateHandler.post(new SaveStateRunnable(groupId));
+ }
}
private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
@@ -3104,6 +3150,23 @@
}
@GuardedBy("mLock")
+ private @NonNull SparseArray<byte[]> saveStateToByteArrayLocked(int userId) {
+ tagProvidersAndHosts();
+
+ final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
+ SparseArray<byte[]> userIdToBytesMapping = new SparseArray<>();
+
+ for (int profileId : profileIds) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ if (writeProfileStateToStreamLocked(outputStream, profileId)) {
+ userIdToBytesMapping.put(profileId, outputStream.toByteArray());
+ }
+ }
+
+ return userIdToBytesMapping;
+ }
+
+ @GuardedBy("mLock")
private void saveStateLocked(int userId) {
tagProvidersAndHosts();
@@ -3117,7 +3180,7 @@
FileOutputStream stream;
try {
stream = file.startWrite();
- if (writeProfileStateToFileLocked(stream, profileId)) {
+ if (writeProfileStateToStreamLocked(stream, profileId)) {
file.finishWrite(stream);
} else {
file.failWrite(stream);
@@ -3158,7 +3221,7 @@
}
@GuardedBy("mLock")
- private boolean writeProfileStateToFileLocked(FileOutputStream stream, int userId) {
+ private boolean writeProfileStateToStreamLocked(OutputStream stream, int userId) {
int N;
try {
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index d9741c8..4a6d5c9b 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -25,9 +25,6 @@
import android.service.autofill.FillResponse;
import android.util.Slog;
-import java.util.Objects;
-
-
/**
* Requests autofill response from a Remote Autofill Service. This autofill service can be
* either a Credential Autofill Service or the user-opted autofill service.
@@ -51,7 +48,6 @@
private final RemoteFillService mRemoteFillService;
private final SecondaryProviderCallback mCallback;
- private FillRequest mLastFillRequest;
private int mLastFlag;
SecondaryProviderHandler(
@@ -97,17 +93,11 @@
}
/**
- * Requests a new fill response. If the fill request is same as the last requested fill request,
- * then the request is duped.
+ * Requests a new fill response.
*/
public void onFillRequest(FillRequest pendingFillRequest, int flag) {
- if (Objects.equals(pendingFillRequest, mLastFillRequest)) {
- Slog.v(TAG, "Deduping fill request to secondary provider.");
- return;
- }
Slog.v(TAG, "Requesting fill response to secondary provider.");
mLastFlag = flag;
- mLastFillRequest = pendingFillRequest;
mRemoteFillService.onFillRequest(pendingFillRequest);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d527ce0..d71258a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -141,6 +141,7 @@
import android.service.autofill.FillEventHistory.Event.NoSaveReason;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
+import android.service.autofill.Flags;
import android.service.autofill.InlinePresentation;
import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
@@ -367,6 +368,9 @@
@GuardedBy("mLock")
private SparseArray<FillResponse> mResponses;
+ @GuardedBy("mLock")
+ private SparseArray<FillResponse> mSecondaryResponses;
+
/**
* Contexts read from the app; they will be updated (sanitized, change values for save) before
* sent to {@link AutofillService}. Ordered by the time they were read.
@@ -576,6 +580,8 @@
@GuardedBy("mLock")
private AutofillId[] mLastFillDialogTriggerIds;
+ private boolean mIgnoreViewStateResetToEmpty;
+
void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill
// returns null, and augmented autofill is triggered, and then the user switches the input
@@ -713,7 +719,14 @@
mPendingFillRequest.getDelayedFillIntentSender());
}
mLastFillRequest = mPendingFillRequest;
- mRemoteFillService.onFillRequest(mPendingFillRequest);
+ if (shouldRequestSecondaryProvider(mPendingFillRequest.getFlags())
+ && mSecondaryProviderHandler != null) {
+ Slog.v(TAG, "Requesting fill response to secondary provider.");
+ mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
+ mPendingFillRequest.getFlags());
+ } else if (mRemoteFillService != null) {
+ mRemoteFillService.onFillRequest(mPendingFillRequest);
+ }
mPendingInlineSuggestionsRequest = null;
mWaitForInlineRequest = false;
mPendingFillRequest = null;
@@ -1196,7 +1209,8 @@
@GuardedBy("mLock")
private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
int flags) {
- final FillResponse existingResponse = viewState.getResponse();
+ final FillResponse existingResponse = shouldRequestSecondaryProvider(flags)
+ ? viewState.getSecondaryResponse() : viewState.getResponse();
mFillRequestEventLogger.startLogForNewRequest();
mRequestCount++;
mFillRequestEventLogger.maybeSetAppPackageUid(uid);
@@ -1430,6 +1444,7 @@
mSessionCommittedEventLogger.maybeSetComponentPackageUid(uid);
mSaveEventLogger = SaveEventLogger.forSessionId(sessionId);
mIsPrimaryCredential = isPrimaryCredential;
+ mIgnoreViewStateResetToEmpty = Flags.ignoreViewStateResetToEmpty();
synchronized (mLock) {
mSessionFlags = new SessionFlags();
@@ -1804,6 +1819,10 @@
return;
}
synchronized (mLock) {
+ if (mSecondaryResponses == null) {
+ mSecondaryResponses = new SparseArray<>(2);
+ }
+ mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse);
setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false,
/* isPrimary= */ false);
@@ -3980,7 +3999,7 @@
}
// If it's not, then check if it should start a partition.
- if (shouldStartNewPartitionLocked(id)) {
+ if (shouldStartNewPartitionLocked(id, flags)) {
if (sDebug) {
Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
+ viewState.getStateAsString());
@@ -4008,9 +4027,11 @@
* @return {@code true} if a new partition should be started
*/
@GuardedBy("mLock")
- private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
+ private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) {
final ViewState currentView = mViewStates.get(id);
- if (mResponses == null) {
+ SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags)
+ ? mSecondaryResponses : mResponses;
+ if (responses == null) {
return currentView != null && (currentView.getState()
& ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0;
}
@@ -4022,7 +4043,7 @@
return true;
}
- final int numResponses = mResponses.size();
+ final int numResponses = responses.size();
if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
+ " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
@@ -4030,7 +4051,7 @@
}
for (int responseNum = 0; responseNum < numResponses; responseNum++) {
- final FillResponse response = mResponses.valueAt(responseNum);
+ final FillResponse response = responses.valueAt(responseNum);
if (ArrayUtils.contains(response.getIgnoredIds(), id)) {
return false;
@@ -4066,6 +4087,10 @@
}
boolean shouldRequestSecondaryProvider(int flags) {
+ if (!mService.isAutofillCredmanIntegrationEnabled()
+ || mSecondaryProviderHandler == null) {
+ return false;
+ }
if (mIsPrimaryCredential) {
return (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) == 0;
} else {
@@ -4205,12 +4230,6 @@
}
break;
case ACTION_VIEW_ENTERED:
- if (shouldRequestSecondaryProvider(flags)
- && mSecondaryProviderHandler != null
- && mAssistReceiver.mLastFillRequest != null) {
- mSecondaryProviderHandler.onFillRequest(mAssistReceiver.mLastFillRequest,
- flags);
- }
mLatencyBaseTime = SystemClock.elapsedRealtime();
boolean wasPreviouslyFillDialog = mPreviouslyFillDialogPotentiallyStarted;
mPreviouslyFillDialogPotentiallyStarted = false;
@@ -4225,6 +4244,19 @@
viewState.setCurrentValue(value);
}
+ if (shouldRequestSecondaryProvider(flags)) {
+ if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(
+ id, viewState, flags)) {
+ Slog.v(TAG, "Started a new fill request for secondary provider.");
+ return;
+ }
+ // If the ViewState is ready to be displayed, onReady() will be called.
+ viewState.update(value, virtualBounds, flags);
+
+ // return here because primary provider logic is not applicable.
+ return;
+ }
+
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
return;
@@ -4391,6 +4423,15 @@
@GuardedBy("mLock")
private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
ViewState viewState, int flags) {
+ if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty())
+ && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText()
+ && viewState.getCurrentValue().getTextValue() != null
+ && viewState.getCurrentValue().getTextValue().length() > 1) {
+ if (sVerbose) {
+ Slog.v(TAG, "Ignoring view state reset to empty on id " + id);
+ }
+ return;
+ }
final String textValue;
if (value == null || !value.isText()) {
textValue = null;
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index b0bb9ec..fec5aa5 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -162,6 +162,11 @@
return mPrimaryFillResponse;
}
+ @Nullable
+ FillResponse getSecondaryResponse() {
+ return mSecondaryFillResponse;
+ }
+
void setResponse(FillResponse response) {
setResponse(response, /* isPrimary= */ true);
}
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index acb5911..d08a97e 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -19,7 +19,13 @@
defaults: ["platform_service_defaults"],
srcs: [":services.backup-sources"],
libs: ["services.core"],
- static_libs: ["app-compat-annotations", "backup_flags_lib"],
+ static_libs: [
+ "app-compat-annotations",
+ "backup_flags_lib",
+ ],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
aconfig_declarations {
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index 550e17b..2bfdd0a 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -31,4 +31,7 @@
"virtualdevice_flags_lib",
"virtual_camera_service_aidl-java",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index db40fc4..6c77018 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Binder.getCallingUid;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
import static com.android.server.companion.CompanionDeviceManagerService.TAG;
@@ -41,6 +42,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.os.Binder;
+import android.os.Process;
import android.util.Log;
import android.util.Slog;
@@ -80,6 +82,11 @@
static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
+ // Allow system server to create CDM associations without FEATURE_COMPANION_DEVICE_SETUP
+ if (getCallingUid() == Process.SYSTEM_UID) {
+ return;
+ }
+
String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP;
FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures;
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index df74770..62c6703 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -130,7 +130,7 @@
synchronized (mTransports) {
for (int i = 0; i < associationIds.length; i++) {
if (mTransports.contains(associationIds[i])) {
- mTransports.get(associationIds[i]).requestForResponse(message, data);
+ mTransports.get(associationIds[i]).sendMessage(message, data);
}
}
}
@@ -220,7 +220,7 @@
if (transport == null) {
return CompletableFuture.failedFuture(new IOException("Missing transport"));
}
- return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
+ return transport.sendMessage(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
}
}
diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java
index 32d4061..22b18ac 100644
--- a/services/companion/java/com/android/server/companion/transport/Transport.java
+++ b/services/companion/java/com/android/server/companion/transport/Transport.java
@@ -16,6 +16,9 @@
package com.android.server.companion.transport;
+import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_FROM_WEARABLE;
+import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_PING;
+import static android.companion.CompanionDeviceManager.MESSAGE_ONEWAY_TO_WEARABLE;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PERMISSION_RESTORE;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_PING;
@@ -80,6 +83,10 @@
return (message & 0xFF000000) == 0x33000000;
}
+ private static boolean isOneway(int message) {
+ return (message & 0xFF000000) == 0x43000000;
+ }
+
@GuardedBy("mPendingRequests")
protected final SparseArray<CompletableFuture<byte[]>> mPendingRequests =
new SparseArray<>();
@@ -134,6 +141,42 @@
protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
throws IOException;
+ /**
+ * Send a message using this transport. If the message was a request, then the returned Future
+ * object will complete successfully only if the remote device both received and processed it
+ * as expected. If the message was a send-and-forget type message, then the Future object will
+ * resolve successfully immediately (with null) upon sending the message.
+ *
+ * @param message the message type
+ * @param data the message payload
+ * @return Future object containing the result of the sent message.
+ */
+ public Future<byte[]> sendMessage(int message, byte[] data) {
+ final CompletableFuture<byte[]> pending = new CompletableFuture<>();
+ if (isOneway(message)) {
+ return sendAndForget(message, data);
+ } else if (isRequest(message)) {
+ return requestForResponse(message, data);
+ } else {
+ Slog.w(TAG, "Failed to send message 0x" + Integer.toHexString(message));
+ pending.completeExceptionally(new IllegalArgumentException(
+ "The message being sent must be either a one-way or a request."
+ ));
+ }
+ return pending;
+ }
+
+ /**
+ * @deprecated Method was renamed to sendMessage(int, byte[]) to support both
+ * send-and-forget type messages as well as wait-for-response type messages.
+ *
+ * @param message request message type
+ * @param data the message payload
+ * @return future object containing the result of the request.
+ *
+ * @see #sendMessage(int, byte[])
+ */
+ @Deprecated
public Future<byte[]> requestForResponse(int message, byte[] data) {
if (DEBUG) Slog.d(TAG, "Requesting for response");
final int sequence = mNextSequence.incrementAndGet();
@@ -154,6 +197,20 @@
return pending;
}
+ private Future<byte[]> sendAndForget(int message, byte[]data) {
+ if (DEBUG) Slog.d(TAG, "Sending a one-way message");
+ final CompletableFuture<byte[]> pending = new CompletableFuture<>();
+
+ try {
+ sendMessage(message, -1, data);
+ pending.complete(null);
+ } catch (IOException e) {
+ pending.completeExceptionally(e);
+ }
+
+ return pending;
+ }
+
protected final void handleMessage(int message, int sequence, @NonNull byte[] data)
throws IOException {
if (DEBUG) {
@@ -162,7 +219,9 @@
+ " from association " + mAssociationId);
}
- if (isRequest(message)) {
+ if (isOneway(message)) {
+ processOneway(message, data);
+ } else if (isRequest(message)) {
try {
processRequest(message, sequence, data);
} catch (IOException e) {
@@ -175,6 +234,21 @@
}
}
+ private void processOneway(int message, byte[] data) {
+ switch (message) {
+ case MESSAGE_ONEWAY_PING:
+ case MESSAGE_ONEWAY_FROM_WEARABLE:
+ case MESSAGE_ONEWAY_TO_WEARABLE: {
+ callback(message, data);
+ break;
+ }
+ default: {
+ Slog.w(TAG, "Ignoring unknown message 0x" + Integer.toHexString(message));
+ break;
+ }
+ }
+ }
+
private void processRequest(int message, int sequence, byte[] data)
throws IOException {
switch (message) {
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 8c728f1..31766f2 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -288,16 +288,15 @@
}
final UserHandle activityUser =
UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid);
+ final ComponentName activityComponent = activityInfo.getComponentName();
+ if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent) && activityUser.isSystem()) {
+ // The error dialog alerting users that streaming is blocked is always allowed.
+ return true;
+ }
if (!mAllowedUsers.contains(activityUser)) {
Slog.d(TAG, "Virtual device launch disallowed from user " + activityUser);
return false;
}
-
- final ComponentName activityComponent = activityInfo.getComponentName();
- if (BLOCKED_APP_STREAMING_COMPONENT.equals(activityComponent)) {
- // The error dialog alerting users that streaming is blocked is always allowed.
- return true;
- }
if (!activityMatchesDisplayCategory(activityInfo)) {
Slog.d(TAG, "The activity's required display category '"
+ activityInfo.requiredDisplayCategory
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index a159a5e..5a548fd 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -54,9 +54,7 @@
"exclude-annotation": "android.support.test.filters.FlakyTest"
}
]
- }
- ],
- "postsubmit": [
+ },
{
"name": "CtsVirtualDevicesCameraTestCases",
"options": [
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
index 6940ffe..f24c4cc 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraConversionUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -70,7 +70,7 @@
@Override
public void onProcessCaptureRequest(int streamId, int frameId) throws RemoteException {
- camera.onProcessCaptureRequest(streamId, frameId, /*metadata=*/ null);
+ camera.onProcessCaptureRequest(streamId, frameId);
}
@Override
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 3a90a95..73ed97f 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -35,8 +35,8 @@
import android.util.LocalLog;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
-import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -123,7 +123,7 @@
public void setContentCaptureEnabledLocked(boolean enabled) {
try {
final Bundle extras = new Bundle();
- extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true);
+ extras.putBoolean(ContentCaptureSession.EXTRA_ENABLED_STATE, true);
mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras);
} catch (RemoteException e) {
Slog.w(TAG, "Error async reporting result to client: " + e);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5111b08..dd001ec 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -212,6 +212,9 @@
"-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_genrule {
@@ -230,6 +233,9 @@
java_library {
name: "services.core",
static_libs: ["services.core.priorityboosted"],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
java_library_host {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index cac2efb..08093c0 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1463,4 +1463,9 @@
*/
@NonNull
public abstract PackageArchiver getPackageArchiver();
+
+ /**
+ * Returns true if the device is upgrading from an SDK version lower than the one specified.
+ */
+ public abstract boolean isUpgradingFromLowerThan(int sdkVersion);
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index c258370..e289a56 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -27,6 +27,7 @@
per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
+per-file *SecurityStateManager* = file:/SECURITY_STATE_OWNERS
per-file *SoundTrigger* = file:/media/java/android/media/soundtrigger/OWNERS
per-file *Storage* = file:/core/java/android/os/storage/OWNERS
per-file *TimeUpdate* = file:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index df8f17a..02f4485 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -70,7 +70,6 @@
import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN;
-import static android.os.PowerExemptionManager.REASON_OTHER;
import static android.os.PowerExemptionManager.REASON_PACKAGE_INSTALLER;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
@@ -127,7 +126,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.Manifest;
-import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -305,6 +303,23 @@
@Retention(RetentionPolicy.SOURCE)
@interface FgsStopReason {}
+ /**
+ * Disables foreground service background starts from BOOT_COMPLETED broadcasts for all types
+ * except:
+ * <ul>
+ * <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION}</li>
+ * <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE}</li>
+ * <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}</li>
+ * <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH}</li>
+ * <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}</li>
+ * <li>{@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE}</li>
+ * </ul>
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM)
+ @Overridable
+ public static final long FGS_BOOT_COMPLETED_RESTRICTIONS = 296558535L;
+
final ActivityManagerService mAm;
// Maximum number of services that we allow to start in the background
@@ -1055,6 +1070,20 @@
}
}
+ private boolean shouldAllowBootCompletedStart(ServiceRecord r, int foregroundServiceType) {
+ @PowerExemptionManager.ReasonCode final int fgsStartReasonCode =
+ r.mInfoTempFgsAllowListReason != null ? r.mInfoTempFgsAllowListReason.mReasonCode
+ : REASON_DENIED;
+ if (Flags.fgsBootCompleted()
+ && CompatChanges.isChangeEnabled(FGS_BOOT_COMPLETED_RESTRICTIONS, r.appInfo.uid)
+ && fgsStartReasonCode == PowerExemptionManager.REASON_BOOT_COMPLETED) {
+ // Filter through types
+ return ((foregroundServiceType & mAm.mConstants.FGS_BOOT_COMPLETED_ALLOWLIST) != 0);
+ }
+ // Not BOOT_COMPLETED
+ return true;
+ }
+
private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service,
int callingUid, int callingPid, String callingProcessName,
int callingProcessState, boolean fgRequired, boolean callerFg,
@@ -1085,7 +1114,7 @@
// Use that as a shortcut if possible to avoid having to recheck all the conditions.
final boolean whileInUseAllowsUiJobScheduling =
ActivityManagerService.doesReasonCodeAllowSchedulingUserInitiatedJobs(
- r.getFgsAllowWIU());
+ r.getFgsAllowWiu_forStart());
r.updateAllowUiJobScheduling(whileInUseAllowsUiJobScheduling
|| mAm.canScheduleUserInitiatedJobs(callingUid, callingPid, callingPackage));
} else {
@@ -2103,6 +2132,12 @@
mServiceFGAnrTimer.cancel(r);
}
+ if (!shouldAllowBootCompletedStart(r, foregroundServiceType)) {
+ throw new ForegroundServiceStartNotAllowedException("FGS type "
+ + ServiceInfo.foregroundServiceTypeToLabel(foregroundServiceType)
+ + " not allowed to start from BOOT_COMPLETED!");
+ }
+
final ProcessServiceRecord psr = r.app.mServices;
try {
boolean ignoreForeground = false;
@@ -2320,7 +2355,7 @@
// If the foreground service is not started from TOP process, do not allow it to
// have while-in-use location/camera/microphone access.
- if (!r.isFgsAllowedWIU()) {
+ if (!r.isFgsAllowedWiu_forCapabilities()) {
Slog.w(TAG,
"Foreground service started from background can not have "
+ "location/camera/microphone access: service "
@@ -2436,7 +2471,7 @@
// mAllowWhileInUsePermissionInFgs.
r.mAllowStartForegroundAtEntering = r.getFgsAllowStart();
r.mAllowWhileInUsePermissionInFgsAtEntering =
- r.isFgsAllowedWIU();
+ r.isFgsAllowedWiu_forCapabilities();
r.mStartForegroundCount++;
r.mFgsEnterTime = SystemClock.uptimeMillis();
if (!stopProcStatsOp) {
@@ -2632,7 +2667,7 @@
policy.getForegroundServiceTypePolicyInfo(type, defaultToType);
final @ForegroundServicePolicyCheckCode int code = policy.checkForegroundServiceTypePolicy(
mAm.mContext, r.packageName, r.app.uid, r.app.getPid(),
- r.isFgsAllowedWIU(), policyInfo);
+ r.isFgsAllowedWiu_forStart(), policyInfo);
RuntimeException exception = null;
switch (code) {
case FGS_TYPE_POLICY_CHECK_DEPRECATED: {
@@ -7580,78 +7615,76 @@
* @param callingUid caller app's uid.
* @param intent intent to start/bind service.
* @param r the service to start.
- * @param isBindService True if it's called from bindService().
+ * @param inBindService True if it's called from bindService().
* @param forBoundFgs set to true if it's called from Service.startForeground() for a
* service that's not started but bound.
- * @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
- BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean inBindService,
boolean forBoundFgs) {
- @ReasonCode int allowWIU;
+ @ReasonCode int allowWiu;
@ReasonCode int allowStart;
// If called from bindService(), do not update the actual fields, but instead
// keep it in a separate set of fields.
- if (isBindService) {
- allowWIU = r.mAllowWIUInBindService;
- allowStart = r.mAllowStartInBindService;
+ if (inBindService) {
+ allowWiu = r.mAllowWiu_inBindService;
+ allowStart = r.mAllowStart_inBindService;
} else {
- allowWIU = r.mAllowWhileInUsePermissionInFgsReasonNoBinding;
- allowStart = r.mAllowStartForegroundNoBinding;
+ allowWiu = r.mAllowWiu_noBinding;
+ allowStart = r.mAllowStart_noBinding;
}
- // Check DeviceConfig flag.
- if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
- if (allowWIU == REASON_DENIED) {
- // BGFGS start restrictions are disabled. We're allowing while-in-use permissions.
- // Note REASON_OTHER since there's no other suitable reason.
- allowWIU = REASON_OTHER;
- }
- }
-
- if ((allowWIU == REASON_DENIED)
- || (allowStart == REASON_DENIED)) {
+ if ((allowWiu == REASON_DENIED) || (allowStart == REASON_DENIED)) {
@ReasonCode final int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges);
// We store them to compare the old and new while-in-use logics to each other.
// (They're not used for any other purposes.)
- if (allowWIU == REASON_DENIED) {
- allowWIU = allowWhileInUse;
+ if (allowWiu == REASON_DENIED) {
+ allowWiu = allowWhileInUse;
}
if (allowStart == REASON_DENIED) {
allowStart = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
- backgroundStartPrivileges, isBindService);
+ backgroundStartPrivileges, inBindService);
}
}
- if (isBindService) {
- r.mAllowWIUInBindService = allowWIU;
- r.mAllowStartInBindService = allowStart;
+ if (inBindService) {
+ r.mAllowWiu_inBindService = allowWiu;
+ r.mAllowStart_inBindService = allowStart;
} else {
if (!forBoundFgs) {
- // This is for "normal" situation.
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU;
- r.mAllowStartForegroundNoBinding = allowStart;
+ // This is for "normal" situation -- either:
+ // - in Context.start[Foreground]Service()
+ // - or, in Service.startForeground() on a started service.
+ r.mAllowWiu_noBinding = allowWiu;
+ r.mAllowStart_noBinding = allowStart;
} else {
- // This logic is only for logging, so we only update the "by-binding" fields.
- if (r.mAllowWIUByBindings == REASON_DENIED) {
- r.mAllowWIUByBindings = allowWIU;
+ // Service.startForeground() is called on a service that's not started, but bound.
+ // In this case, we set them to "byBindings", not "noBinding", because
+ // we don't want to use them when we calculate the "legacy" code.
+ //
+ // We don't want to set them to "no binding" codes, because on U-QPR1 and below,
+ // we didn't call setFgsRestrictionLocked() in the code path which sets
+ // forBoundFgs to true, and we wanted to preserve the original behavior in other
+ // places to compare the legacy and new logic.
+ if (r.mAllowWiu_byBindings == REASON_DENIED) {
+ r.mAllowWiu_byBindings = allowWiu;
}
- if (r.mAllowStartByBindings == REASON_DENIED) {
- r.mAllowStartByBindings = allowStart;
+ if (r.mAllowStart_byBindings == REASON_DENIED) {
+ r.mAllowStart_byBindings = allowStart;
}
}
// Also do a binding client check, unless called from bindService().
- if (r.mAllowWIUByBindings == REASON_DENIED) {
- r.mAllowWIUByBindings =
+ if (r.mAllowWiu_byBindings == REASON_DENIED) {
+ r.mAllowWiu_byBindings =
shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid);
}
- if (r.mAllowStartByBindings == REASON_DENIED) {
- r.mAllowStartByBindings = r.mAllowWIUByBindings;
+ if (r.mAllowStart_byBindings == REASON_DENIED) {
+ r.mAllowStart_byBindings = r.mAllowWiu_byBindings;
}
}
}
@@ -7660,13 +7693,13 @@
* Reset various while-in-use and BFSL related information.
*/
void resetFgsRestrictionLocked(ServiceRecord r) {
- r.clearFgsAllowWIU();
+ r.clearFgsAllowWiu();
r.clearFgsAllowStart();
r.mInfoAllowStartForeground = null;
r.mInfoTempFgsAllowListReason = null;
r.mLoggedInfoAllowStartForeground = false;
- r.updateAllowUiJobScheduling(r.isFgsAllowedWIU());
+ r.updateAllowUiJobScheduling(r.isFgsAllowedWiu_forStart());
}
boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) {
@@ -8284,7 +8317,8 @@
allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgsAtEntering;
fgsStartReasonCode = r.mAllowStartForegroundAtEntering;
} else {
- allowWhileInUsePermissionInFgs = r.isFgsAllowedWIU();
+ // TODO: Also log "forStart"
+ allowWhileInUsePermissionInFgs = r.isFgsAllowedWiu_forCapabilities();
fgsStartReasonCode = r.getFgsAllowStart();
}
final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null
@@ -8306,7 +8340,7 @@
r.mFgsNotificationShown,
durationMs,
r.mStartForegroundCount,
- ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+ 0, // Short instance name -- no longer logging it.
r.mFgsHasNotificationPermission,
r.foregroundServiceType,
fgsTypeCheckCode,
@@ -8323,12 +8357,12 @@
mAm.getUidProcessCapabilityLocked(r.mRecentCallingUid),
0,
0,
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding,
- r.mAllowWIUInBindService,
- r.mAllowWIUByBindings,
- r.mAllowStartForegroundNoBinding,
- r.mAllowStartInBindService,
- r.mAllowStartByBindings,
+ r.mAllowWiu_noBinding,
+ r.mAllowWiu_inBindService,
+ r.mAllowWiu_byBindings,
+ r.mAllowStart_noBinding,
+ r.mAllowStart_inBindService,
+ r.mAllowStart_byBindings,
fgsStartApi,
fgsRestrictionRecalculated);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3ce91c8..8ad60e6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,12 @@
package com.android.server.am;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
@@ -73,6 +79,9 @@
= "fgservice_screen_on_before_time";
private static final String KEY_FGSERVICE_SCREEN_ON_AFTER_TIME
= "fgservice_screen_on_after_time";
+
+ private static final String KEY_FGS_BOOT_COMPLETED_ALLOWLIST = "fgs_boot_completed_allowlist";
+
private static final String KEY_CONTENT_PROVIDER_RETAIN_TIME = "content_provider_retain_time";
private static final String KEY_GC_TIMEOUT = "gc_timeout";
private static final String KEY_GC_MIN_INTERVAL = "gc_min_interval";
@@ -166,6 +175,15 @@
private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
private static final long DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME = 5*1000;
+
+ private static final int DEFAULT_FGS_BOOT_COMPLETED_ALLOWLIST =
+ FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
+ | FOREGROUND_SERVICE_TYPE_HEALTH
+ | FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING
+ | FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
+ | FOREGROUND_SERVICE_TYPE_SPECIAL_USE
+ | FOREGROUND_SERVICE_TYPE_LOCATION;
+
private static final long DEFAULT_CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
private static final long DEFAULT_GC_TIMEOUT = 5*1000;
private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000;
@@ -446,6 +464,9 @@
// on until we will stop reporting it.
public long FGSERVICE_SCREEN_ON_AFTER_TIME = DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME;
+ // Allow-list for FGS types that are allowed to start from BOOT_COMPLETED.
+ public int FGS_BOOT_COMPLETED_ALLOWLIST = DEFAULT_FGS_BOOT_COMPLETED_ALLOWLIST;
+
// How long we will retain processes hosting content providers in the "last activity"
// state before allowing them to drop down to the regular cached LRU list. This is
// to avoid thrashing of provider processes under low memory situations.
@@ -629,6 +650,10 @@
// foreground service background start restriction.
volatile boolean mFgsStartRestrictionNotificationEnabled = false;
+ // Indicates whether PSS profiling in AppProfiler is force-enabled, even if RSS is used by
+ // default. Controlled by Settings.Global.FORCE_ENABLE_PSS_PROFILING
+ volatile boolean mForceEnablePssProfiling = false;
+
/**
* Indicates whether the foreground service background start restriction is enabled for
* caller app that is targeting S+.
@@ -958,6 +983,9 @@
private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
+ private static final Uri FORCE_ENABLE_PSS_PROFILING_URI =
+ Settings.Global.getUriFor(Settings.Global.FORCE_ENABLE_PSS_PROFILING);
+
/**
* The threshold to decide if a given association should be dumped into metrics.
*/
@@ -1368,6 +1396,7 @@
mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI,
false, this);
}
+ mResolver.registerContentObserver(FORCE_ENABLE_PSS_PROFILING_URI, false, this);
updateConstants();
if (mSystemServerAutomaticHeapDumpEnabled) {
updateEnableAutomaticSystemServerHeapDumps();
@@ -1383,6 +1412,7 @@
// The following read from Settings.
updateActivityStartsLoggingEnabled();
updateForegroundServiceStartsLoggingEnabled();
+ updateForceEnablePssProfiling();
// Read DropboxRateLimiter params from flags.
mService.initDropboxRateLimiter();
}
@@ -1424,6 +1454,8 @@
updateForegroundServiceStartsLoggingEnabled();
} else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) {
updateEnableAutomaticSystemServerHeapDumps();
+ } else if (FORCE_ENABLE_PSS_PROFILING_URI.equals(uri)) {
+ updateForceEnablePssProfiling();
}
}
@@ -1450,6 +1482,8 @@
DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME);
FGSERVICE_SCREEN_ON_AFTER_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME,
DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME);
+ FGS_BOOT_COMPLETED_ALLOWLIST = mParser.getInt(KEY_FGS_BOOT_COMPLETED_ALLOWLIST,
+ DEFAULT_FGS_BOOT_COMPLETED_ALLOWLIST);
CONTENT_PROVIDER_RETAIN_TIME = mParser.getLong(KEY_CONTENT_PROVIDER_RETAIN_TIME,
DEFAULT_CONTENT_PROVIDER_RETAIN_TIME);
GC_TIMEOUT = mParser.getLong(KEY_GC_TIMEOUT,
@@ -1536,6 +1570,11 @@
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 1) == 1;
}
+ private void updateForceEnablePssProfiling() {
+ mForceEnablePssProfiling = Settings.Global.getInt(mResolver,
+ Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1;
+ }
+
private void updateBackgroundActivityStarts() {
mFlagBackgroundActivityStartsEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2091,6 +2130,8 @@
pw.println(FGSERVICE_SCREEN_ON_BEFORE_TIME);
pw.print(" "); pw.print(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME); pw.print("=");
pw.println(FGSERVICE_SCREEN_ON_AFTER_TIME);
+ pw.print(" "); pw.print(KEY_FGS_BOOT_COMPLETED_ALLOWLIST); pw.print("=");
+ pw.println(FGS_BOOT_COMPLETED_ALLOWLIST);
pw.print(" "); pw.print(KEY_CONTENT_PROVIDER_RETAIN_TIME); pw.print("=");
pw.println(CONTENT_PROVIDER_RETAIN_TIME);
pw.print(" "); pw.print(KEY_GC_TIMEOUT); pw.print("=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a80d2fd..fddb570 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -164,6 +164,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.MemoryStatUtil.hasMemcg;
import static com.android.server.am.ProcessList.ProcStartHandler;
+import static com.android.server.flags.Flags.disableSystemCompaction;
import static com.android.server.net.NetworkPolicyManagerInternal.updateBlockedReasonsWithProcState;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND;
@@ -326,7 +327,6 @@
import android.os.DropBoxManager;
import android.os.FactoryTest;
import android.os.FileUtils;
-import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.IDeviceIdentifiersPolicyService;
@@ -1221,7 +1221,7 @@
* by the user ID the sticky is for, and can include UserHandle.USER_ALL
* for stickies that are sent to all users.
*/
- @GuardedBy("this")
+ @GuardedBy("mStickyBroadcasts")
final SparseArray<ArrayMap<String, ArrayList<StickyBroadcast>>> mStickyBroadcasts =
new SparseArray<>();
@@ -4398,7 +4398,9 @@
if (packageName == null) {
// Remove all sticky broadcasts from this user.
- mStickyBroadcasts.remove(userId);
+ synchronized (mStickyBroadcasts) {
+ mStickyBroadcasts.remove(userId);
+ }
}
ArrayList<ContentProviderRecord> providers = new ArrayList<>();
@@ -8564,8 +8566,10 @@
final long now = SystemClock.uptimeMillis();
final long timeSinceLastIdle = now - mLastIdleTime;
- // Compact all non-zygote processes to freshen up the page cache.
- mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
+ if (!disableSystemCompaction()) {
+ // Compact all non-zygote processes to freshen up the page cache.
+ mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
+ }
final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLPr(now);
mLastIdleTime = now;
@@ -8605,7 +8609,7 @@
final long initialIdlePssOrRss, lastPssOrRss, lastSwapPss;
synchronized (mAppProfiler.mProfilerLock) {
initialIdlePssOrRss = pr.getInitialIdlePssOrRss();
- lastPssOrRss = !Flags.removeAppProfilerPssCollection()
+ lastPssOrRss = mAppProfiler.isProfilingPss()
? pr.getLastPss() : pr.getLastRss();
lastSwapPss = pr.getLastSwapPss();
}
@@ -8615,14 +8619,14 @@
final StringBuilder sb2 = new StringBuilder(128);
sb2.append("Kill");
sb2.append(proc.processName);
- if (!Flags.removeAppProfilerPssCollection()) {
+ if (mAppProfiler.isProfilingPss()) {
sb2.append(" in idle maint: pss=");
} else {
sb2.append(" in idle maint: rss=");
}
sb2.append(lastPssOrRss);
- if (!Flags.removeAppProfilerPssCollection()) {
+ if (mAppProfiler.isProfilingPss()) {
sb2.append(", swapPss=");
sb2.append(lastSwapPss);
sb2.append(", initialPss=");
@@ -8637,7 +8641,7 @@
Slog.wtfQuiet(TAG, sb2.toString());
mHandler.post(() -> {
synchronized (ActivityManagerService.this) {
- proc.killLocked(!Flags.removeAppProfilerPssCollection()
+ proc.killLocked(mAppProfiler.isProfilingPss()
? "idle maint (pss " : "idle maint (rss " + lastPssOrRss
+ " from " + initialIdlePssOrRss + ")",
ApplicationExitInfo.REASON_OTHER,
@@ -11333,20 +11337,23 @@
for (BroadcastQueue q : mBroadcastQueues) {
q.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
}
- for (int user=0; user<mStickyBroadcasts.size(); user++) {
- long token = proto.start(ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
- proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
- for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
- : mStickyBroadcasts.valueAt(user).entrySet()) {
- long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
- proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
- for (StickyBroadcast broadcast : ent.getValue()) {
- broadcast.intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS,
- false, true, true, false);
+ synchronized (mStickyBroadcasts) {
+ for (int user = 0; user < mStickyBroadcasts.size(); user++) {
+ long token = proto.start(
+ ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
+ proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
+ for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
+ : mStickyBroadcasts.valueAt(user).entrySet()) {
+ long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
+ proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
+ for (StickyBroadcast broadcast : ent.getValue()) {
+ broadcast.intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS,
+ false, true, true, false);
+ }
+ proto.end(actionToken);
}
- proto.end(actionToken);
+ proto.end(token);
}
- proto.end(token);
}
long handlerToken = proto.start(ActivityManagerServiceDumpBroadcastsProto.HANDLER);
@@ -11486,45 +11493,50 @@
needSep = true;
- if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null && dumpPackage == null) {
- for (int user=0; user<mStickyBroadcasts.size(); user++) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- printedAnything = true;
- pw.print(" Sticky broadcasts for user ");
- pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":");
- StringBuilder sb = new StringBuilder(128);
- for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
- : mStickyBroadcasts.valueAt(user).entrySet()) {
- pw.print(" * Sticky action "); pw.print(ent.getKey());
- if (dumpAll) {
- pw.println(":");
- ArrayList<StickyBroadcast> broadcasts = ent.getValue();
- final int N = broadcasts.size();
- for (int i=0; i<N; i++) {
- final Intent intent = broadcasts.get(i).intent;
- final boolean deferUntilActive = broadcasts.get(i).deferUntilActive;
- sb.setLength(0);
- sb.append(" Intent: ");
- intent.toShortString(sb, false, true, false, false);
- pw.print(sb);
- if (deferUntilActive) {
- pw.print(" [D]");
+ synchronized (mStickyBroadcasts) {
+ if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null
+ && dumpPackage == null) {
+ for (int user = 0; user < mStickyBroadcasts.size(); user++) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ printedAnything = true;
+ pw.print(" Sticky broadcasts for user ");
+ pw.print(mStickyBroadcasts.keyAt(user));
+ pw.println(":");
+ StringBuilder sb = new StringBuilder(128);
+ for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
+ : mStickyBroadcasts.valueAt(user).entrySet()) {
+ pw.print(" * Sticky action ");
+ pw.print(ent.getKey());
+ if (dumpAll) {
+ pw.println(":");
+ ArrayList<StickyBroadcast> broadcasts = ent.getValue();
+ final int N = broadcasts.size();
+ for (int i = 0; i < N; i++) {
+ final Intent intent = broadcasts.get(i).intent;
+ final boolean deferUntilActive = broadcasts.get(i).deferUntilActive;
+ sb.setLength(0);
+ sb.append(" Intent: ");
+ intent.toShortString(sb, false, true, false, false);
+ pw.print(sb);
+ if (deferUntilActive) {
+ pw.print(" [D]");
+ }
+ pw.println();
+ pw.print(" originalCallingUid: ");
+ pw.println(broadcasts.get(i).originalCallingUid);
+ pw.println();
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: ");
+ pw.println(bundle);
+ }
}
- pw.println();
- pw.print(" originalCallingUid: ");
- pw.println(broadcasts.get(i).originalCallingUid);
- pw.println();
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- pw.print(" extras: ");
- pw.println(bundle);
- }
+ } else {
+ pw.println("");
}
- } else {
- pw.println("");
}
}
}
@@ -14189,7 +14201,7 @@
int callingUid;
int callingPid;
boolean instantApp;
- synchronized(this) {
+ synchronized (mProcLock) {
callerApp = getRecordForAppLOSP(caller);
if (callerApp == null) {
Slog.w(TAG, "registerReceiverWithFeature: no app for " + caller);
@@ -14205,57 +14217,59 @@
callingPid = callerApp.getPid();
instantApp = isInstantApp(callerApp, callerPackage, callingUid);
- userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
- ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
+ }
+ userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+ ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
- // Warn if system internals are registering for important broadcasts
- // without also using a priority to ensure they process the event
- // before normal apps hear about it
- if (UserHandle.isCore(callingUid)) {
- final int priority = filter.getPriority();
- final boolean systemPriority = (priority >= IntentFilter.SYSTEM_HIGH_PRIORITY)
- || (priority <= IntentFilter.SYSTEM_LOW_PRIORITY);
- if (!systemPriority) {
- final int N = filter.countActions();
- for (int i = 0; i < N; i++) {
- // TODO: expand to additional important broadcasts over time
- final String action = filter.getAction(i);
- if (action.startsWith("android.intent.action.USER_")
- || action.startsWith("android.intent.action.PACKAGE_")
- || action.startsWith("android.intent.action.UID_")
- || action.startsWith("android.intent.action.EXTERNAL_")
- || action.startsWith("android.bluetooth.")
- || action.equals(Intent.ACTION_SHUTDOWN)) {
- if (DEBUG_BROADCAST) {
- Slog.wtf(TAG,
- "System internals registering for " + filter.toLongString()
- + " with app priority; this will race with apps!",
- new Throwable());
- }
-
- // When undefined, assume that system internals need
- // to hear about the event first; they can use
- // SYSTEM_LOW_PRIORITY if they need to hear last
- if (priority == 0) {
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- }
- break;
+ // Warn if system internals are registering for important broadcasts
+ // without also using a priority to ensure they process the event
+ // before normal apps hear about it
+ if (UserHandle.isCore(callingUid)) {
+ final int priority = filter.getPriority();
+ final boolean systemPriority = (priority >= IntentFilter.SYSTEM_HIGH_PRIORITY)
+ || (priority <= IntentFilter.SYSTEM_LOW_PRIORITY);
+ if (!systemPriority) {
+ final int N = filter.countActions();
+ for (int i = 0; i < N; i++) {
+ // TODO: expand to additional important broadcasts over time
+ final String action = filter.getAction(i);
+ if (action.startsWith("android.intent.action.USER_")
+ || action.startsWith("android.intent.action.PACKAGE_")
+ || action.startsWith("android.intent.action.UID_")
+ || action.startsWith("android.intent.action.EXTERNAL_")
+ || action.startsWith("android.bluetooth.")
+ || action.equals(Intent.ACTION_SHUTDOWN)) {
+ if (DEBUG_BROADCAST) {
+ Slog.wtf(TAG,
+ "System internals registering for " + filter.toLongString()
+ + " with app priority; this will race with apps!",
+ new Throwable());
}
+
+ // When undefined, assume that system internals need
+ // to hear about the event first; they can use
+ // SYSTEM_LOW_PRIORITY if they need to hear last
+ if (priority == 0) {
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ }
+ break;
}
}
}
+ }
- Iterator<String> actions = filter.actionsIterator();
- if (actions == null) {
- ArrayList<String> noAction = new ArrayList<String>(1);
- noAction.add(null);
- actions = noAction.iterator();
- }
- boolean onlyProtectedBroadcasts = true;
+ Iterator<String> actions = filter.actionsIterator();
+ if (actions == null) {
+ ArrayList<String> noAction = new ArrayList<String>(1);
+ noAction.add(null);
+ actions = noAction.iterator();
+ }
+ boolean onlyProtectedBroadcasts = true;
- // Collect stickies of users and check if broadcast is only registered for protected
- // broadcasts
- int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
+ // Collect stickies of users and check if broadcast is only registered for protected
+ // broadcasts
+ int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
+ synchronized (mStickyBroadcasts) {
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
@@ -14281,68 +14295,68 @@
}
}
}
+ }
- if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
- SdkSandboxManagerLocal sdkSandboxManagerLocal =
- LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
- if (sdkSandboxManagerLocal == null) {
- throw new IllegalStateException("SdkSandboxManagerLocal not found when checking"
- + " whether SDK sandbox uid can register to broadcast receivers.");
- }
- if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
- /*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
- throw new SecurityException("SDK sandbox not allowed to register receiver"
- + " with the given IntentFilter: " + filter.toLongString());
- }
+ if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
+ SdkSandboxManagerLocal sdkSandboxManagerLocal =
+ LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
+ if (sdkSandboxManagerLocal == null) {
+ throw new IllegalStateException("SdkSandboxManagerLocal not found when checking"
+ + " whether SDK sandbox uid can register to broadcast receivers.");
}
-
- // If the change is enabled, but neither exported or not exported is set, we need to log
- // an error so the consumer can know to explicitly set the value for their flag.
- // If the caller is registering for a sticky broadcast with a null receiver, we won't
- // require a flag
- final boolean explicitExportStateDefined =
- (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
- if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
- (flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
- throw new IllegalArgumentException(
- "Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
- + "flag");
+ if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
+ /*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
+ throw new SecurityException("SDK sandbox not allowed to register receiver"
+ + " with the given IntentFilter: " + filter.toLongString());
}
+ }
- // Don't enforce the flag check if we're EITHER registering for only protected
- // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
- // not be used generally, so we will be marking them as exported by default
- boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
- DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
+ // If the change is enabled, but neither exported or not exported is set, we need to log
+ // an error so the consumer can know to explicitly set the value for their flag.
+ // If the caller is registering for a sticky broadcast with a null receiver, we won't
+ // require a flag
+ final boolean explicitExportStateDefined =
+ (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
+ if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
+ (flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
+ throw new IllegalArgumentException(
+ "Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+ + "flag");
+ }
- // A receiver that is visible to instant apps must also be exported.
- final boolean unexportedReceiverVisibleToInstantApps =
- ((flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0) && (
- (flags & Context.RECEIVER_NOT_EXPORTED) != 0);
- if (unexportedReceiverVisibleToInstantApps && requireExplicitFlagForDynamicReceivers) {
- throw new IllegalArgumentException(
- "Receiver can't specify both RECEIVER_VISIBLE_TO_INSTANT_APPS and "
- + "RECEIVER_NOT_EXPORTED flag");
- }
+ // Don't enforce the flag check if we're EITHER registering for only protected
+ // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
+ // not be used generally, so we will be marking them as exported by default
+ boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
+ DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
- if (!onlyProtectedBroadcasts) {
- if (receiver == null && !explicitExportStateDefined) {
- // sticky broadcast, no flag specified (flag isn't required)
- flags |= Context.RECEIVER_EXPORTED;
- } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
- throw new SecurityException(
- callerPackage + ": One of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED should be specified when a receiver "
- + "isn't being registered exclusively for system broadcasts");
- // Assume default behavior-- flag check is not enforced
- } else if (!requireExplicitFlagForDynamicReceivers && (
- (flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
- // Change is not enabled, assume exported unless otherwise specified.
- flags |= Context.RECEIVER_EXPORTED;
- }
- } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
+ // A receiver that is visible to instant apps must also be exported.
+ final boolean unexportedReceiverVisibleToInstantApps =
+ ((flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0) && (
+ (flags & Context.RECEIVER_NOT_EXPORTED) != 0);
+ if (unexportedReceiverVisibleToInstantApps && requireExplicitFlagForDynamicReceivers) {
+ throw new IllegalArgumentException(
+ "Receiver can't specify both RECEIVER_VISIBLE_TO_INSTANT_APPS and "
+ + "RECEIVER_NOT_EXPORTED flag");
+ }
+
+ if (!onlyProtectedBroadcasts) {
+ if (receiver == null && !explicitExportStateDefined) {
+ // sticky broadcast, no flag specified (flag isn't required)
+ flags |= Context.RECEIVER_EXPORTED;
+ } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
+ throw new SecurityException(
+ callerPackage + ": One of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED should be specified when a receiver "
+ + "isn't being registered exclusively for system broadcasts");
+ // Assume default behavior-- flag check is not enforced
+ } else if (!requireExplicitFlagForDynamicReceivers && (
+ (flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
+ // Change is not enabled, assume exported unless otherwise specified.
flags |= Context.RECEIVER_EXPORTED;
}
+ } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
+ flags |= Context.RECEIVER_EXPORTED;
}
// Dynamic receivers are exported by default for versions prior to T
@@ -15332,55 +15346,58 @@
throw new SecurityException(
"Sticky broadcasts can't target a specific component");
}
- // We use userId directly here, since the "all" target is maintained
- // as a separate set of sticky broadcasts.
- if (userId != UserHandle.USER_ALL) {
- // But first, if this is not a broadcast to all users, then
- // make sure it doesn't conflict with an existing broadcast to
- // all users.
- ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(
- UserHandle.USER_ALL);
- if (stickies != null) {
- ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
- if (list != null) {
- int N = list.size();
- int i;
- for (i=0; i<N; i++) {
- if (intent.filterEquals(list.get(i).intent)) {
- throw new IllegalArgumentException(
- "Sticky broadcast " + intent + " for user "
- + userId + " conflicts with existing global broadcast");
+ synchronized (mStickyBroadcasts) {
+ // We use userId directly here, since the "all" target is maintained
+ // as a separate set of sticky broadcasts.
+ if (userId != UserHandle.USER_ALL) {
+ // But first, if this is not a broadcast to all users, then
+ // make sure it doesn't conflict with an existing broadcast to
+ // all users.
+ ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(
+ UserHandle.USER_ALL);
+ if (stickies != null) {
+ ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
+ if (list != null) {
+ int N = list.size();
+ int i;
+ for (i = 0; i < N; i++) {
+ if (intent.filterEquals(list.get(i).intent)) {
+ throw new IllegalArgumentException("Sticky broadcast " + intent
+ + " for user " + userId
+ + " conflicts with existing global broadcast");
+ }
}
}
}
}
- }
- ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
- if (stickies == null) {
- stickies = new ArrayMap<>();
- mStickyBroadcasts.put(userId, stickies);
- }
- ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
- if (list == null) {
- list = new ArrayList<>();
- stickies.put(intent.getAction(), list);
- }
- final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive(
- callingUid, brOptions, resultTo, ordered,
- BroadcastRecord.calculateUrgent(intent, brOptions));
- final int stickiesCount = list.size();
- int i;
- for (i = 0; i < stickiesCount; i++) {
- if (intent.filterEquals(list.get(i).intent)) {
- // This sticky already exists, replace it.
- list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive,
- callingUid, callerAppProcessState));
- break;
+ ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
+ mStickyBroadcasts.get(userId);
+ if (stickies == null) {
+ stickies = new ArrayMap<>();
+ mStickyBroadcasts.put(userId, stickies);
}
- }
- if (i >= stickiesCount) {
- list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid,
- callerAppProcessState));
+ ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
+ if (list == null) {
+ list = new ArrayList<>();
+ stickies.put(intent.getAction(), list);
+ }
+ final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive(
+ callingUid, brOptions, resultTo, ordered,
+ BroadcastRecord.calculateUrgent(intent, brOptions));
+ final int stickiesCount = list.size();
+ int i;
+ for (i = 0; i < stickiesCount; i++) {
+ if (intent.filterEquals(list.get(i).intent)) {
+ // This sticky already exists, replace it.
+ list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive,
+ callingUid, callerAppProcessState));
+ break;
+ }
+ }
+ if (i >= stickiesCount) {
+ list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive,
+ callingUid, callerAppProcessState));
+ }
}
}
@@ -15617,13 +15634,15 @@
}
@VisibleForTesting
- ArrayList<StickyBroadcast> getStickyBroadcasts(String action, int userId) {
- final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts =
- mStickyBroadcasts.get(userId);
- if (stickyBroadcasts == null) {
- return null;
+ ArrayList<StickyBroadcast> getStickyBroadcastsForTest(String action, int userId) {
+ synchronized (mStickyBroadcasts) {
+ final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts =
+ mStickyBroadcasts.get(userId);
+ if (stickyBroadcasts == null) {
+ return null;
+ }
+ return stickyBroadcasts.get(action);
}
- return stickyBroadcasts.get(action);
}
/**
@@ -15788,6 +15807,7 @@
}
}
+ @Override
public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
@@ -15797,23 +15817,23 @@
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_NON_FULL, "removeStickyBroadcast", null);
- synchronized(this) {
- if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: unbroadcastIntent() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.BROADCAST_STICKY;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: unbroadcastIntent() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.BROADCAST_STICKY;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ synchronized (mStickyBroadcasts) {
ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
if (stickies != null) {
ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
if (list != null) {
int N = list.size();
int i;
- for (i=0; i<N; i++) {
+ for (i = 0; i < N; i++) {
if (intent.filterEquals(list.get(i).intent)) {
list.remove(i);
break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index ae0cd65..848a2b0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -30,6 +30,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Process.INVALID_UID;
import static android.view.Display.INVALID_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
@@ -555,6 +556,13 @@
} else if (opt.equals("--dismiss-keyguard-if-insecure")
|| opt.equals("--dismiss-keyguard")) {
mDismissKeyguardIfInsecure = true;
+ } else if (opt.equals("--allow-fgs-start-reason")) {
+ final int reasonCode = Integer.parseInt(getNextArgRequired());
+ mBroadcastOptions = BroadcastOptions.makeBasic();
+ mBroadcastOptions.setTemporaryAppAllowlist(10_000,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ reasonCode,
+ "");
} else {
return false;
}
@@ -2238,6 +2246,7 @@
pw.println("Performing idle maintenance...");
mInterface.sendIdleJobTrigger();
+ mInternal.performIdleMaintenance();
return 0;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java
index 01466b8..78a2ecb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerUtils.java
+++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java
@@ -129,14 +129,6 @@
}
/**
- * @param shortInstanceName {@link ServiceRecord#shortInstanceName}.
- * @return hash of the ServiceRecord's shortInstanceName, combined with ANDROID_ID.
- */
- public static int hashComponentNameForAtom(String shortInstanceName) {
- return getUnsignedHashUnCached(shortInstanceName) ^ getAndroidIdHash();
- }
-
- /**
* Helper method to log an unsafe intent event.
*/
public static void logUnsafeIntentEvent(int event, int callingUid,
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 2e0aec9..e4956b3 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -602,7 +602,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case COLLECT_PSS_BG_MSG:
- if (!Flags.removeAppProfilerPssCollection()) {
+ if (isProfilingPss()) {
collectPssInBackground();
} else {
collectRssInBackground();
@@ -748,6 +748,11 @@
} while (true);
}
+ boolean isProfilingPss() {
+ return !Flags.removeAppProfilerPssCollection()
+ || mService.mConstants.mForceEnablePssProfiling;
+ }
+
// This method is analogous to collectPssInBackground() and is intended to be used as a
// replacement if Flags.removeAppProfilerPssCollection() is enabled. References to PSS in
// methods outside of AppProfiler have generally been kept where a new RSS equivalent is not
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 0b56146..c37e619 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -353,8 +353,7 @@
// If we come across the record that's being enqueued in the queue, then that means
// we already enqueued it for a receiver in this process and trying to insert a new
// one past this could create priority inversion in the queue, so bail out.
- if (record == testRecord && record.blockedUntilBeyondCount[recordIndex]
- > testRecord.blockedUntilBeyondCount[testRecordIndex]) {
+ if (record == testRecord) {
break;
}
if ((record.callingUid == testRecord.callingUid)
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index d0ab287..626b70b 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -275,7 +275,7 @@
@VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
@VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 10_000L;
- @VisibleForTesting static final boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true;
+ @VisibleForTesting static final boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = false;
@VisibleForTesting static final boolean DEFAULT_FREEZER_BINDER_ENABLED = true;
@VisibleForTesting static final long DEFAULT_FREEZER_BINDER_DIVISOR = 4;
@VisibleForTesting static final int DEFAULT_FREEZER_BINDER_OFFSET = 500;
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index fc8ad6bc..b68572f 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -507,7 +507,8 @@
r.appInfo.uid,
r.shortInstanceName,
fgsState, // FGS State
- r.isFgsAllowedWIU(), // allowWhileInUsePermissionInFgs
+ // TODO: Also log "forStart"
+ r.isFgsAllowedWiu_forCapabilities(), // allowWhileInUsePermissionInFgs
r.getFgsAllowStart(), // fgsStartReasonCode
r.appInfo.targetSdkVersion,
r.mRecentCallingUid,
@@ -518,7 +519,7 @@
r.mFgsNotificationShown,
0, // durationMs
r.mStartForegroundCount,
- ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+ 0, // Short instance name -- no longer logging it.
r.mFgsHasNotificationPermission,
r.foregroundServiceType,
0,
@@ -535,12 +536,12 @@
ActivityManager.PROCESS_CAPABILITY_NONE,
apiDurationBeforeFgsStart,
apiDurationAfterFgsEnd,
- r.mAllowWhileInUsePermissionInFgsReasonNoBinding,
- r.mAllowWIUInBindService,
- r.mAllowWIUByBindings,
- r.mAllowStartForegroundNoBinding,
- r.mAllowStartInBindService,
- r.mAllowStartByBindings,
+ r.mAllowWiu_noBinding,
+ r.mAllowWiu_inBindService,
+ r.mAllowWiu_byBindings,
+ r.mAllowStart_noBinding,
+ r.mAllowStart_inBindService,
+ r.mAllowStart_byBindings,
FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
false
);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 3424578a..f49e25a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -143,7 +143,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.net.NetworkPolicyManager;
-import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManagerInternal;
@@ -2254,7 +2253,7 @@
if (s.isForeground) {
final int fgsType = s.foregroundServiceType;
- if (s.isFgsAllowedWIU()) {
+ if (s.isFgsAllowedWiu_forCapabilities()) {
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
@@ -2418,7 +2417,7 @@
// normally be a B service, but if we are low on RAM and it
// is large we want to force it down since we would prefer to
// keep launcher over it.
- long lastPssOrRss = !Flags.removeAppProfilerPssCollection()
+ long lastPssOrRss = mService.mAppProfiler.isProfilingPss()
? app.mProfile.getLastPss() : app.mProfile.getLastRss();
// RSS is larger than PSS, but the RSS/PSS ratio varies per-process based on how
@@ -2427,9 +2426,8 @@
//
// TODO(b/296454553): Tune the second value so that the relative number of
// service B is similar before/after this flag is enabled.
- double thresholdModifier = !Flags.removeAppProfilerPssCollection()
- ? 1
- : mConstants.PSS_TO_RSS_THRESHOLD_MODIFIER;
+ double thresholdModifier = mService.mAppProfiler.isProfilingPss()
+ ? 1 : mConstants.PSS_TO_RSS_THRESHOLD_MODIFIER;
double cachedRestoreThreshold =
mProcessList.getCachedRestoreThresholdKb() * thresholdModifier;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index e57206e..b03183c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -94,7 +94,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.DropBoxManager;
-import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -2482,7 +2481,6 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
app.getDisabledCompatChanges(),
- bindOverrideSysprops,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else if (hostingRecord.usesAppZygote()) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
@@ -4733,7 +4731,7 @@
pw.print("state: cur="); pw.print(makeProcStateString(state.getCurProcState()));
pw.print(" set="); pw.print(makeProcStateString(state.getSetProcState()));
// These values won't be collected if the flag is enabled.
- if (!Flags.removeAppProfilerPssCollection()) {
+ if (service.mAppProfiler.isProfilingPss()) {
pw.print(" lastPss=");
DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
pw.print(" lastSwapPss=");
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 8ca64f8..d8f797c 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -23,7 +23,6 @@
import android.app.ProcessMemoryState.HostingComponentType;
import android.content.pm.ApplicationInfo;
import android.os.Debug;
-import android.os.Flags;
import android.os.Process;
import android.os.SystemClock;
import android.util.DebugUtils;
@@ -677,7 +676,7 @@
void dumpPss(PrintWriter pw, String prefix, long nowUptime) {
synchronized (mProfilerLock) {
// TODO(b/297542292): Remove this case once PSS profiling is replaced
- if (!Flags.removeAppProfilerPssCollection()) {
+ if (mService.mAppProfiler.isProfilingPss()) {
pw.print(prefix);
pw.print("lastPssTime=");
TimeUtils.formatDuration(mLastPssTime, nowUptime, pw);
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 5ad921f..3391ec7 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -29,7 +29,6 @@
import android.annotation.ElapsedRealtimeLong;
import android.app.ActivityManager;
import android.content.ComponentName;
-import android.os.Flags;
import android.os.SystemClock;
import android.os.Trace;
import android.util.Slog;
@@ -1351,7 +1350,7 @@
}
if (mNotCachedSinceIdle) {
pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(mNotCachedSinceIdle);
- if (!Flags.removeAppProfilerPssCollection()) {
+ if (mService.mAppProfiler.isProfilingPss()) {
pw.print(" initialIdlePss=");
} else {
pw.print(" initialIdleRss=");
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 0fba739..08b129e 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -37,6 +37,9 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -81,6 +84,37 @@
// Maximum number of times it can fail during execution before giving up.
static final int MAX_DONE_EXECUTING_COUNT = 6;
+
+ // Compat IDs for the new FGS logic. For now, we just disable all of them.
+ // TODO: Enable them at some point, but only for V+ builds.
+
+ /**
+ * Compat ID to enable the new FGS start logic, for permission calculation used for
+ * per-FGS-type eligibility calculation.
+ * (See also android.app.ForegroundServiceTypePolicy)
+ */
+ @ChangeId
+ // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Disabled
+ static final long USE_NEW_WIU_LOGIC_FOR_START = 311208629L;
+
+ /**
+ * Compat ID to enable the new FGS start logic, for capability calculation.
+ */
+ @ChangeId
+ // Always enabled
+ @Disabled
+ static final long USE_NEW_WIU_LOGIC_FOR_CAPABILITIES = 313677553L;
+
+ /**
+ * Compat ID to enable the new FGS start logic, for deciding whether to allow FGS start from
+ * the background.
+ */
+ @ChangeId
+ // @EnabledAfter(targetSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Disabled
+ static final long USE_NEW_BFSL_LOGIC = 311208749L;
+
final ActivityManagerService ams;
final ComponentName name; // service component.
final ComponentName instanceName; // service component's per-instance name.
@@ -178,7 +212,7 @@
// If it's not DENIED, while-in-use permissions are allowed.
// while-in-use permissions in FGS started from background might be restricted.
@PowerExemptionManager.ReasonCode
- int mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED;
+ int mAllowWiu_noBinding = REASON_DENIED;
// A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
boolean mAllowWhileInUsePermissionInFgsAtEntering;
@@ -208,7 +242,7 @@
// allow the service becomes foreground service? Service started from background may not be
// allowed to become a foreground service.
@PowerExemptionManager.ReasonCode
- int mAllowStartForegroundNoBinding = REASON_DENIED;
+ int mAllowStart_noBinding = REASON_DENIED;
// A copy of mAllowStartForeground's value when the service is entering FGS state.
@PowerExemptionManager.ReasonCode
int mAllowStartForegroundAtEntering = REASON_DENIED;
@@ -220,72 +254,172 @@
boolean mLoggedInfoAllowStartForeground;
@PowerExemptionManager.ReasonCode
- int mAllowWIUInBindService = REASON_DENIED;
+ int mAllowWiu_inBindService = REASON_DENIED;
@PowerExemptionManager.ReasonCode
- int mAllowWIUByBindings = REASON_DENIED;
+ int mAllowWiu_byBindings = REASON_DENIED;
@PowerExemptionManager.ReasonCode
- int mAllowStartInBindService = REASON_DENIED;
+ int mAllowStart_inBindService = REASON_DENIED;
@PowerExemptionManager.ReasonCode
- int mAllowStartByBindings = REASON_DENIED;
+ int mAllowStart_byBindings = REASON_DENIED;
- @PowerExemptionManager.ReasonCode
- int getFgsAllowWIU() {
- return mAllowWhileInUsePermissionInFgsReasonNoBinding != REASON_DENIED
- ? mAllowWhileInUsePermissionInFgsReasonNoBinding
- : mAllowWIUInBindService;
+ /**
+ * Whether to use the new "while-in-use permission" logic for FGS start
+ */
+ private boolean useNewWiuLogic_forStart() {
+ return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
+ && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_START, appInfo.uid);
}
- boolean isFgsAllowedWIU() {
- return getFgsAllowWIU() != REASON_DENIED;
+ /**
+ * Whether to use the new "while-in-use permission" logic for capabilities
+ */
+ private boolean useNewWiuLogic_forCapabilities() {
+ return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
+ && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_CAPABILITIES, appInfo.uid);
}
+ /**
+ * Whether to use the new "FGS BG start exemption" logic.
+ */
+ private boolean useNewBfslLogic() {
+ return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
+ && CompatChanges.isChangeEnabled(USE_NEW_BFSL_LOGIC, appInfo.uid);
+ }
+
+
+ @PowerExemptionManager.ReasonCode
+ private int getFgsAllowWiu_legacy() {
+ // In the legacy mode (U-), we use mAllowWiu_inBindService and mAllowWiu_noBinding.
+ return reasonOr(
+ mAllowWiu_noBinding,
+ mAllowWiu_inBindService
+ );
+ }
+
+ @PowerExemptionManager.ReasonCode
+ private int getFgsAllowWiu_new() {
+ // In the new mode, use by-bindings instead of in-bind-service
+ return reasonOr(
+ mAllowWiu_noBinding,
+ mAllowWiu_byBindings
+ );
+ }
+
+ /**
+ * We use this logic for ForegroundServiceTypePolicy and UIDT eligibility check.
+ */
+ @PowerExemptionManager.ReasonCode
+ int getFgsAllowWiu_forStart() {
+ if (useNewWiuLogic_forStart()) {
+ return getFgsAllowWiu_new();
+ } else {
+ return getFgsAllowWiu_legacy();
+ }
+ }
+
+ /**
+ * We use this logic for the capability calculation in oom-adjuster.
+ */
+ @PowerExemptionManager.ReasonCode
+ int getFgsAllowWiu_forCapabilities() {
+ if (useNewWiuLogic_forCapabilities()) {
+ return getFgsAllowWiu_new();
+ } else {
+ // If alwaysUseNewLogicForWiuCapabilities() isn't set, just use the same logic as
+ // getFgsAllowWiu_forStart().
+ return getFgsAllowWiu_forStart();
+ }
+ }
+
+ /**
+ * We use this logic for ForegroundServiceTypePolicy and UIDT eligibility check.
+ */
+ boolean isFgsAllowedWiu_forStart() {
+ return getFgsAllowWiu_forStart() != REASON_DENIED;
+ }
+
+ /**
+ * We use this logic for the capability calculation in oom-adjuster.
+ */
+ boolean isFgsAllowedWiu_forCapabilities() {
+ return getFgsAllowWiu_forCapabilities() != REASON_DENIED;
+ }
+
+ @PowerExemptionManager.ReasonCode
+ private int getFgsAllowStart_legacy() {
+ // This is used for BFSL (background FGS launch) exemption.
+ // Originally -- on U-QPR1 and before -- we only used [in-bind-service] + [no-binding].
+ // This would exclude certain "valid" situations, so in U-QPR2, we started
+ // using [by-bindings] too.
+ return reasonOr(
+ mAllowStart_noBinding,
+ mAllowStart_inBindService,
+ mAllowStart_byBindings
+ );
+ }
+
+ @PowerExemptionManager.ReasonCode
+ private int getFgsAllowStart_new() {
+ // In the new mode, we don't use [in-bind-service].
+ return reasonOr(
+ mAllowStart_noBinding,
+ mAllowStart_byBindings
+ );
+ }
+
+ /**
+ * Calculate a BFSL exemption code.
+ */
@PowerExemptionManager.ReasonCode
int getFgsAllowStart() {
- return mAllowStartForegroundNoBinding != REASON_DENIED
- ? mAllowStartForegroundNoBinding
- : (mAllowStartByBindings != REASON_DENIED
- ? mAllowStartByBindings
- : mAllowStartInBindService);
+ if (useNewBfslLogic()) {
+ return getFgsAllowStart_new();
+ } else {
+ return getFgsAllowStart_legacy();
+ }
}
+ /**
+ * Return whether BFSL is allowed or not.
+ */
boolean isFgsAllowedStart() {
return getFgsAllowStart() != REASON_DENIED;
}
- void clearFgsAllowWIU() {
- mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED;
- mAllowWIUInBindService = REASON_DENIED;
- mAllowWIUByBindings = REASON_DENIED;
+ void clearFgsAllowWiu() {
+ mAllowWiu_noBinding = REASON_DENIED;
+ mAllowWiu_inBindService = REASON_DENIED;
+ mAllowWiu_byBindings = REASON_DENIED;
}
void clearFgsAllowStart() {
- mAllowStartForegroundNoBinding = REASON_DENIED;
- mAllowStartInBindService = REASON_DENIED;
- mAllowStartByBindings = REASON_DENIED;
+ mAllowStart_noBinding = REASON_DENIED;
+ mAllowStart_inBindService = REASON_DENIED;
+ mAllowStart_byBindings = REASON_DENIED;
}
@PowerExemptionManager.ReasonCode
- int reasonOr(@PowerExemptionManager.ReasonCode int first,
+ static int reasonOr(
+ @PowerExemptionManager.ReasonCode int first,
@PowerExemptionManager.ReasonCode int second) {
return first != REASON_DENIED ? first : second;
}
- boolean allowedChanged(@PowerExemptionManager.ReasonCode int first,
- @PowerExemptionManager.ReasonCode int second) {
- return (first == REASON_DENIED) != (second == REASON_DENIED);
+ @PowerExemptionManager.ReasonCode
+ static int reasonOr(
+ @PowerExemptionManager.ReasonCode int first,
+ @PowerExemptionManager.ReasonCode int second,
+ @PowerExemptionManager.ReasonCode int third) {
+ return first != REASON_DENIED ? first : reasonOr(second, third);
}
- String changeMessage(@PowerExemptionManager.ReasonCode int first,
- @PowerExemptionManager.ReasonCode int second) {
- return reasonOr(first, second) == REASON_DENIED ? "DENIED"
- : ("ALLOWED ("
- + reasonCodeToString(first)
- + "+"
- + reasonCodeToString(second)
- + ")");
+ boolean allowedChanged(
+ @PowerExemptionManager.ReasonCode int legacyCode,
+ @PowerExemptionManager.ReasonCode int newCode) {
+ return (legacyCode == REASON_DENIED) != (newCode == REASON_DENIED);
}
private String getFgsInfoForWtf() {
@@ -295,15 +429,14 @@
}
void maybeLogFgsLogicChange() {
- final int origWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding,
- mAllowWIUInBindService);
- final int newWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding,
- mAllowWIUByBindings);
- final int origStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartInBindService);
- final int newStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartByBindings);
+ final int wiuLegacy = getFgsAllowWiu_legacy();
+ final int wiuNew = getFgsAllowWiu_new();
- final boolean wiuChanged = allowedChanged(origWiu, newWiu);
- final boolean startChanged = allowedChanged(origStart, newStart);
+ final int startLegacy = getFgsAllowStart_legacy();
+ final int startNew = getFgsAllowStart_new();
+
+ final boolean wiuChanged = allowedChanged(wiuLegacy, wiuNew);
+ final boolean startChanged = allowedChanged(startLegacy, startNew);
if (!wiuChanged && !startChanged) {
return;
@@ -311,15 +444,14 @@
final String message = "FGS logic changed:"
+ (wiuChanged ? " [WIU changed]" : "")
+ (startChanged ? " [BFSL changed]" : "")
- + " OW:" // Orig-WIU
- + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding,
- mAllowWIUInBindService)
- + " NW:" // New-WIU
- + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding, mAllowWIUByBindings)
- + " OS:" // Orig-start
- + changeMessage(mAllowStartForegroundNoBinding, mAllowStartInBindService)
- + " NS:" // New-start
- + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings)
+ + " Orig WIU:"
+ + reasonCodeToString(wiuLegacy)
+ + " New WIU:"
+ + reasonCodeToString(wiuNew)
+ + " Orig BFSL:"
+ + reasonCodeToString(startLegacy)
+ + " New BFSL:"
+ + reasonCodeToString(startNew)
+ getFgsInfoForWtf();
Slog.wtf(TAG_SERVICE, message);
}
@@ -587,6 +719,7 @@
proto.write(ServiceRecordProto.AppInfo.RES_DIR, appInfo.publicSourceDir);
}
proto.write(ServiceRecordProto.AppInfo.DATA_DIR, appInfo.dataDir);
+ proto.write(ServiceRecordProto.AppInfo.TARGET_SDK_VERSION, appInfo.targetSdkVersion);
proto.end(appInfoToken);
}
if (app != null) {
@@ -611,8 +744,10 @@
ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now);
ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
+
+ // TODO: Log "forStart" too.
proto.write(ServiceRecordProto.ALLOW_WHILE_IN_USE_PERMISSION_IN_FGS,
- isFgsAllowedWIU());
+ isFgsAllowedWiu_forCapabilities());
if (startRequested || delayedStop || lastStartId != 0) {
long startToken = proto.start(ServiceRecordProto.START);
@@ -691,15 +826,25 @@
proto.end(shortFgsToken);
}
+ // TODO: Dump all the mAllowWiu* and mAllowStart* fields as needed.
+
proto.end(token);
}
+ void dumpReasonCode(PrintWriter pw, String prefix, String fieldName, int code) {
+ pw.print(prefix);
+ pw.print(fieldName);
+ pw.print("=");
+ pw.println(PowerExemptionManager.reasonCodeToString(code));
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("intent={");
pw.print(intent.getIntent().toShortString(false, true, false, false));
pw.println('}');
pw.print(prefix); pw.print("packageName="); pw.println(packageName);
pw.print(prefix); pw.print("processName="); pw.println(processName);
+ pw.print(prefix); pw.print("targetSdkVersion="); pw.println(appInfo.targetSdkVersion);
if (permission != null) {
pw.print(prefix); pw.print("permission="); pw.println(permission);
}
@@ -727,26 +872,38 @@
pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
pw.println(mBackgroundStartPrivilegesByStartMerged);
}
- pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
- pw.println(PowerExemptionManager.reasonCodeToString(
- mAllowWhileInUsePermissionInFgsReasonNoBinding));
- pw.print(prefix); pw.print("mAllowWIUInBindService=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUInBindService));
- pw.print(prefix); pw.print("mAllowWIUByBindings=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUByBindings));
+ pw.print(prefix); pw.print("useNewWiuLogic_forCapabilities()=");
+ pw.println(useNewWiuLogic_forCapabilities());
+ pw.print(prefix); pw.print("useNewWiuLogic_forStart()=");
+ pw.println(useNewWiuLogic_forStart());
+ pw.print(prefix); pw.print("useNewBfslLogic()=");
+ pw.println(useNewBfslLogic());
+
+ dumpReasonCode(pw, prefix, "mAllowWiu_noBinding", mAllowWiu_noBinding);
+ dumpReasonCode(pw, prefix, "mAllowWiu_inBindService", mAllowWiu_inBindService);
+ dumpReasonCode(pw, prefix, "mAllowWiu_byBindings", mAllowWiu_byBindings);
+
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_legacy", getFgsAllowWiu_legacy());
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_new", getFgsAllowWiu_new());
+
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_forStart", getFgsAllowWiu_forStart());
+ dumpReasonCode(pw, prefix, "getFgsAllowWiu_forCapabilities",
+ getFgsAllowWiu_forCapabilities());
pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
pw.print(prefix); pw.print("recentCallingPackage=");
pw.println(mRecentCallingPackage);
pw.print(prefix); pw.print("recentCallingUid=");
pw.println(mRecentCallingUid);
- pw.print(prefix); pw.print("allowStartForeground=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartForegroundNoBinding));
- pw.print(prefix); pw.print("mAllowStartInBindService=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartInBindService));
- pw.print(prefix); pw.print("mAllowStartByBindings=");
- pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartByBindings));
+
+ dumpReasonCode(pw, prefix, "mAllowStart_noBinding", mAllowStart_noBinding);
+ dumpReasonCode(pw, prefix, "mAllowStart_inBindService", mAllowStart_inBindService);
+ dumpReasonCode(pw, prefix, "mAllowStart_byBindings", mAllowStart_byBindings);
+
+ dumpReasonCode(pw, prefix, "getFgsAllowStart_legacy", getFgsAllowStart_legacy());
+ dumpReasonCode(pw, prefix, "getFgsAllowStart_new", getFgsAllowStart_new());
+ dumpReasonCode(pw, prefix, "getFgsAllowStart", getFgsAllowStart());
pw.print(prefix); pw.print("startForegroundCount=");
pw.println(mStartForegroundCount);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 3e1edf2..d0d647c 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -141,6 +141,7 @@
"context_hub",
"core_experiments_team_internal",
"core_graphics",
+ "core_libraries",
"dck_framework",
"devoptions_settings",
"game",
@@ -178,6 +179,7 @@
"text",
"threadnetwork",
"tv_system_ui",
+ "usb",
"vibrator",
"virtual_devices",
"wallet_integration",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a6b532c..2b81dbc 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2059,9 +2059,6 @@
mTargetUserId = targetUserId;
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
- if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
- mInjector.setHasTopUi(true);
- }
if (userSwitchUiEnabled) {
UserInfo currentUserInfo = getUserInfo(currentUserId);
Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
@@ -2130,9 +2127,6 @@
}
private void endUserSwitch() {
- if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
- mInjector.setHasTopUi(false);
- }
final int nextUserId;
synchronized (mLock) {
nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL);
@@ -3050,8 +3044,8 @@
/**
* Returns whether the given user requires credential entry at this time. This is used to
- * intercept activity launches for locked work apps due to work challenge being triggered
- * or when the profile user is yet to be unlocked.
+ * intercept activity launches for apps corresponding to locked profiles due to separate
+ * challenge being triggered or when the profile user is yet to be unlocked.
*/
protected boolean shouldConfirmCredentials(@UserIdInt int userId) {
if (getStartedUserState(userId) == null) {
@@ -3816,15 +3810,6 @@
getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
}
- void setHasTopUi(boolean hasTopUi) {
- try {
- Slogf.i(TAG, "Setting hasTopUi to " + hasTopUi);
- mService.setHasTopUi(hasTopUi);
- } catch (RemoteException e) {
- Slogf.e(TAG, "Failed to allow using all CPU cores", e);
- }
- }
-
void onSystemUserVisibilityChanged(boolean visible) {
getUserManagerInternal().onSystemUserVisibilityChanged(visible);
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index d9e8ddd..654aebd 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -28,3 +28,10 @@
description: "Restrict network access for certain applications in BFGS process state"
bug: "304347838"
}
+# Whether to use the new while-in-use / BG-FGS-start logic
+flag {
+ namespace: "backstage_power"
+ name: "new_fgs_restriction_logic"
+ description: "Enable the new FGS restriction logic"
+ bug: "276963716"
+}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index b3fb9c9..8b7e56e 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -20,6 +20,7 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.server.app.Flags.gameDefaultFrameRate;
+import static android.server.app.Flags.disableGameModeWhenAppTop;
import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
@@ -181,7 +182,9 @@
@Nullable
final MyUidObserver mUidObserver;
@GuardedBy("mUidObserverLock")
- private final Set<Integer> mForegroundGameUids = new HashSet<>();
+ private final Set<Integer> mGameForegroundUids = new HashSet<>();
+ @GuardedBy("mUidObserverLock")
+ private final Set<Integer> mNonGameForegroundUids = new HashSet<>();
private final GameManagerServiceSystemPropertiesWrapper mSysProps;
private float mGameDefaultFrameRateValue;
@@ -238,12 +241,10 @@
FileUtils.S_IRUSR | FileUtils.S_IWUSR
| FileUtils.S_IRGRP | FileUtils.S_IWGRP,
-1, -1);
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
mGameServiceController = new GameServiceController(
context, BackgroundThread.getExecutor(),
- new GameServiceProviderSelectorImpl(
- context.getResources(),
- context.getPackageManager()),
+ new GameServiceProviderSelectorImpl(context.getResources(), mPackageManager),
new GameServiceProviderInstanceFactoryImpl(context));
} else {
mGameServiceController = null;
@@ -2245,7 +2246,7 @@
// Update all foreground games' frame rate.
synchronized (mUidObserverLock) {
- for (int uid : mForegroundGameUids) {
+ for (int uid : mGameForegroundUids) {
setGameDefaultFrameRateOverride(uid, getGameDefaultFrameRate(isEnabled));
}
}
@@ -2261,31 +2262,44 @@
@Override
public void onUidGone(int uid, boolean disabled) {
synchronized (mUidObserverLock) {
- disableGameMode(uid);
+ handleUidMovedOffTop(uid);
}
}
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+ switch (procState) {
+ case ActivityManager.PROCESS_STATE_TOP:
+ handleUidMovedToTop(uid);
+ return;
+ default:
+ handleUidMovedOffTop(uid);
+ }
+ }
+
+ private void handleUidMovedToTop(int uid) {
+ final String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages == null || packages.length == 0) {
+ return;
+ }
+
+ final int userId = mContext.getUserId();
+ final boolean isNotGame = Arrays.stream(packages).noneMatch(
+ p -> isPackageGame(p, userId));
synchronized (mUidObserverLock) {
-
- if (procState != ActivityManager.PROCESS_STATE_TOP) {
- disableGameMode(uid);
+ if (isNotGame) {
+ if (disableGameModeWhenAppTop()) {
+ if (!mGameForegroundUids.isEmpty() && mNonGameForegroundUids.isEmpty()) {
+ Slog.v(TAG, "Game power mode OFF (first non-game in foreground)");
+ mPowerManagerInternal.setPowerMode(Mode.GAME, false);
+ }
+ mNonGameForegroundUids.add(uid);
+ }
return;
}
-
- final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
- if (packages == null || packages.length == 0) {
- return;
- }
-
- final int userId = mContext.getUserId();
- if (!Arrays.stream(packages).anyMatch(p -> isPackageGame(p, userId))) {
- return;
- }
-
- if (mForegroundGameUids.isEmpty()) {
- Slog.v(TAG, "Game power mode ON (process state was changed to foreground)");
+ if (mGameForegroundUids.isEmpty() && (!disableGameModeWhenAppTop()
+ || mNonGameForegroundUids.isEmpty())) {
+ Slog.v(TAG, "Game power mode ON (first game in foreground)");
mPowerManagerInternal.setPowerMode(Mode.GAME, true);
}
final boolean isGameDefaultFrameRateDisabled =
@@ -2293,22 +2307,26 @@
PROPERTY_DEBUG_GFX_GAME_DEFAULT_FRAME_RATE_DISABLED, false);
setGameDefaultFrameRateOverride(uid,
getGameDefaultFrameRate(!isGameDefaultFrameRateDisabled));
- mForegroundGameUids.add(uid);
+ mGameForegroundUids.add(uid);
}
}
- private void disableGameMode(int uid) {
+ private void handleUidMovedOffTop(int uid) {
synchronized (mUidObserverLock) {
- if (!mForegroundGameUids.contains(uid)) {
- return;
+ if (mGameForegroundUids.contains(uid)) {
+ mGameForegroundUids.remove(uid);
+ if (mGameForegroundUids.isEmpty() && (!disableGameModeWhenAppTop()
+ || mNonGameForegroundUids.isEmpty())) {
+ Slog.v(TAG, "Game power mode OFF (no games in foreground)");
+ mPowerManagerInternal.setPowerMode(Mode.GAME, false);
+ }
+ } else if (disableGameModeWhenAppTop() && mNonGameForegroundUids.contains(uid)) {
+ mNonGameForegroundUids.remove(uid);
+ if (mNonGameForegroundUids.isEmpty() && !mGameForegroundUids.isEmpty()) {
+ Slog.v(TAG, "Game power mode ON (only games in foreground)");
+ mPowerManagerInternal.setPowerMode(Mode.GAME, true);
+ }
}
- mForegroundGameUids.remove(uid);
- if (!mForegroundGameUids.isEmpty()) {
- return;
- }
- Slog.v(TAG,
- "Game power mode OFF (process remomved or state changed to background)");
- mPowerManagerInternal.setPowerMode(Mode.GAME, false);
}
}
}
diff --git a/services/core/java/com/android/server/app/flags.aconfig b/services/core/java/com/android/server/app/flags.aconfig
index f2e4783..0673013 100644
--- a/services/core/java/com/android/server/app/flags.aconfig
+++ b/services/core/java/com/android/server/app/flags.aconfig
@@ -6,4 +6,11 @@
description: "This flag guards the new behavior with the addition of Game Default Frame Rate feature."
bug: "286084594"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+ name: "disable_game_mode_when_app_top"
+ namespace: "game"
+ description: "Disable game power mode when a non-game app is also top and visible"
+ bug: "299295925"
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 8091753..dada72e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -56,6 +56,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.PrintWriterPrinter;
import com.android.internal.annotations.GuardedBy;
@@ -1640,7 +1641,7 @@
return mBtHelper.getLeAudioDeviceGroupId(device);
}
- /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
+ /*package*/ List<Pair<String, String>> getLeAudioGroupAddresses(int groupId) {
return mBtHelper.getLeAudioGroupAddresses(groupId);
}
@@ -2651,9 +2652,9 @@
}
}
- List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) {
synchronized (mDeviceStateLock) {
- return mDeviceInventory.getDeviceAddresses(device);
+ return mDeviceInventory.getDeviceIdentityAddresses(device);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index d138f24..e05824a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -566,23 +566,40 @@
final int mDeviceType;
final @NonNull String mDeviceName;
final @NonNull String mDeviceAddress;
+ @NonNull String mDeviceIdentityAddress;
int mDeviceCodecFormat;
- @NonNull String mPeerDeviceAddress;
final int mGroupId;
+ @NonNull String mPeerDeviceAddress;
+ @NonNull String mPeerIdentityDeviceAddress;
/** Disabled operating modes for this device. Use a negative logic so that by default
* an empty list means all modes are allowed.
* See BluetoothAdapter.AUDIO_MODE_DUPLEX and BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY */
@NonNull ArraySet<String> mDisabledModes = new ArraySet(0);
- DeviceInfo(int deviceType, String deviceName, String deviceAddress,
- int deviceCodecFormat, String peerDeviceAddress, int groupId) {
+ DeviceInfo(int deviceType, String deviceName, String address,
+ String identityAddress, int codecFormat,
+ int groupId, String peerAddress, String peerIdentityAddress) {
mDeviceType = deviceType;
- mDeviceName = deviceName == null ? "" : deviceName;
- mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
- mDeviceCodecFormat = deviceCodecFormat;
- mPeerDeviceAddress = peerDeviceAddress == null ? "" : peerDeviceAddress;
+ mDeviceName = TextUtils.emptyIfNull(deviceName);
+ mDeviceAddress = TextUtils.emptyIfNull(address);
+ mDeviceIdentityAddress = TextUtils.emptyIfNull(identityAddress);
+ mDeviceCodecFormat = codecFormat;
mGroupId = groupId;
+ mPeerDeviceAddress = TextUtils.emptyIfNull(peerAddress);
+ mPeerIdentityDeviceAddress = TextUtils.emptyIfNull(peerIdentityAddress);
+ }
+
+ /** Constructor for all devices except A2DP sink and LE Audio */
+ DeviceInfo(int deviceType, String deviceName, String address) {
+ this(deviceType, deviceName, address, null, AudioSystem.AUDIO_FORMAT_DEFAULT);
+ }
+
+ /** Constructor for A2DP sink devices */
+ DeviceInfo(int deviceType, String deviceName, String address,
+ String identityAddress, int codecFormat) {
+ this(deviceType, deviceName, address, identityAddress, codecFormat,
+ BluetoothLeAudio.GROUP_ID_INVALID, null, null);
}
void setModeDisabled(String mode) {
@@ -601,25 +618,20 @@
return isModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
}
- DeviceInfo(int deviceType, String deviceName, String deviceAddress,
- int deviceCodecFormat) {
- this(deviceType, deviceName, deviceAddress, deviceCodecFormat,
- null, BluetoothLeAudio.GROUP_ID_INVALID);
- }
-
- DeviceInfo(int deviceType, String deviceName, String deviceAddress) {
- this(deviceType, deviceName, deviceAddress, AudioSystem.AUDIO_FORMAT_DEFAULT);
- }
-
@Override
public String toString() {
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
+ " (" + AudioSystem.getDeviceName(mDeviceType)
+ ") name:" + mDeviceName
+ " addr:" + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceAddress)
+ + " identity addr:"
+ + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceIdentityAddress)
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
- + " peer addr:" + mPeerDeviceAddress
+ " group:" + mGroupId
+ + " peer addr:"
+ + Utils.anonymizeBluetoothAddress(mDeviceType, mPeerDeviceAddress)
+ + " peer identity addr:"
+ + Utils.anonymizeBluetoothAddress(mDeviceType, mPeerIdentityDeviceAddress)
+ " disabled modes: " + mDisabledModes + "]";
}
@@ -947,20 +959,44 @@
}
+ /**
+ * Goes over all connected LE Audio devices in the provided group ID and
+ * update:
+ * - the peer address according to the addres of other device in the same
+ * group (can also clear the peer address is not anymore in the group)
+ * - The dentity address if not yet set.
+ * LE Audio buds in a pair are in the same group.
+ * @param groupId the LE Audio group to update
+ */
/*package*/ void onUpdateLeAudioGroupAddresses(int groupId) {
synchronized (mDevicesLock) {
+ // <address, identy address>
+ List<Pair<String, String>> addresses = new ArrayList<>();
for (DeviceInfo di : mConnectedDevices.values()) {
if (di.mGroupId == groupId) {
- List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ if (addresses.isEmpty()) {
+ addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ }
if (di.mPeerDeviceAddress.equals("")) {
- for (String addr : addresses) {
- if (!addr.equals(di.mDeviceAddress)) {
- di.mPeerDeviceAddress = addr;
+ for (Pair<String, String> addr : addresses) {
+ if (!addr.first.equals(di.mDeviceAddress)) {
+ di.mPeerDeviceAddress = addr.first;
+ di.mPeerIdentityDeviceAddress = addr.second;
break;
}
}
- } else if (!addresses.contains(di.mPeerDeviceAddress)) {
+ } else if (!addresses.contains(
+ new Pair(di.mPeerDeviceAddress, di.mPeerIdentityDeviceAddress))) {
di.mPeerDeviceAddress = "";
+ di.mPeerIdentityDeviceAddress = "";
+ }
+ if (di.mDeviceIdentityAddress.equals("")) {
+ for (Pair<String, String> addr : addresses) {
+ if (addr.first.equals(di.mDeviceAddress)) {
+ di.mDeviceIdentityAddress = addr.second;
+ break;
+ }
+ }
}
}
}
@@ -1964,7 +2000,7 @@
mDeviceBroker.clearA2dpSuspended(true /* internalOnly */);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
- address, codec);
+ address, btInfo.mDevice.getIdentityAddress(), codec);
final String diKey = di.getKey();
mConnectedDevices.put(diKey, di);
// on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -2381,12 +2417,15 @@
// Find LE Group ID and peer headset address if available
final int groupId = mDeviceBroker.getLeAudioDeviceGroupId(btInfo.mDevice);
String peerAddress = "";
+ String peerIdentityAddress = "";
if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
- List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ List<Pair<String, String>> addresses =
+ mDeviceBroker.getLeAudioGroupAddresses(groupId);
if (addresses.size() > 1) {
- for (String addr : addresses) {
- if (!addr.equals(address)) {
- peerAddress = addr;
+ for (Pair<String, String> addr : addresses) {
+ if (!addr.first.equals(address)) {
+ peerAddress = addr.first;
+ peerIdentityAddress = addr.second;
break;
}
}
@@ -2420,8 +2459,9 @@
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
- new DeviceInfo(device, name, address, codec,
- peerAddress, groupId));
+ new DeviceInfo(device, name, address,
+ btInfo.mDevice.getIdentityAddress(), codec,
+ groupId, peerAddress, peerIdentityAddress));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
addAudioDeviceInInventoryIfNeeded(device, address, peerAddress,
@@ -2806,18 +2846,19 @@
mDevRoleCapturePresetDispatchers.finishBroadcast();
}
- List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) {
List<String> addresses = new ArrayList<String>();
final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
device.getAddress());
synchronized (mDevicesLock) {
DeviceInfo di = mConnectedDevices.get(key);
if (di != null) {
- if (!di.mDeviceAddress.isEmpty()) {
- addresses.add(di.mDeviceAddress);
+ if (!di.mDeviceIdentityAddress.isEmpty()) {
+ addresses.add(di.mDeviceIdentityAddress);
}
- if (!di.mPeerDeviceAddress.isEmpty()) {
- addresses.add(di.mPeerDeviceAddress);
+ if (!di.mPeerIdentityDeviceAddress.isEmpty()
+ && !di.mPeerIdentityDeviceAddress.equals(di.mDeviceIdentityAddress)) {
+ addresses.add(di.mPeerIdentityDeviceAddress);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0c78231..7ac4dd3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -33,6 +33,7 @@
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import static android.media.AudioManager.STREAM_SYSTEM;
+import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.INVALID_UID;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
@@ -43,6 +44,7 @@
import static com.android.media.audio.Flags.alarmMinVolumeZero;
import static com.android.media.audio.Flags.bluetoothMacAddressAnonymization;
import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
+import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
import static com.android.server.utils.EventLogger.Event.ALOGE;
import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -116,6 +118,7 @@
import android.media.AudioSystem;
import android.media.AudioTrack;
import android.media.BluetoothProfileConnectionInfo;
+import android.media.FadeManagerConfiguration;
import android.media.IAudioDeviceVolumeDispatcher;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
@@ -605,6 +608,7 @@
};
private final boolean mUseFixedVolume;
+ private final boolean mRingerModeAffectsAlarm;
private final boolean mUseVolumeGroupAliases;
// If absolute volume is supported in AVRCP device
@@ -1298,6 +1302,9 @@
mUseFixedVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
+ mRingerModeAffectsAlarm = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_audio_ringer_mode_affects_alarm_stream);
+
mRecordMonitor = new RecordingActivityMonitor(mContext);
mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
@@ -4513,6 +4520,8 @@
+ bluetoothMacAddressAnonymization());
pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:"
+ disablePrescaleAbsoluteVolume());
+ pw.println("\tandroid.media.audiopolicy.enableFadeManagerConfiguration:"
+ + enableFadeManagerConfiguration());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -7015,6 +7024,19 @@
ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_DTMF);
}
+ if (ringerModeAffectsAlarm()) {
+ if (mRingerModeAffectsAlarm) {
+ boolean muteAlarmWithRinger =
+ mSettings.getGlobalInt(mContentResolver,
+ Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE,
+ /* def= */ 0) != 0;
+ if (muteAlarmWithRinger) {
+ ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_ALARM);
+ } else {
+ ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_ALARM);
+ }
+ }
+ }
if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
mSettings.putSystemIntForUser(mContentResolver,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
@@ -9674,6 +9696,8 @@
Settings.Global.ZEN_MODE), false, this);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.ZEN_MODE_CONFIG_ETAG), false, this);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE), false, this);
mContentResolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
@@ -12614,17 +12638,62 @@
}
/**
+ * see {@link AudioPolicy#setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)}
+ */
+ @android.annotation.EnforcePermission(
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public int setFadeManagerConfigurationForFocusLoss(
+ @NonNull FadeManagerConfiguration fmcForFocusLoss) {
+ super.setFadeManagerConfigurationForFocusLoss_enforcePermission();
+ ensureFadeManagerConfigIsEnabled();
+ Objects.requireNonNull(fmcForFocusLoss,
+ "Fade manager config for focus loss cannot be null");
+ validateFadeManagerConfiguration(fmcForFocusLoss);
+
+ return mPlaybackMonitor.setFadeManagerConfiguration(AudioManager.AUDIOFOCUS_LOSS,
+ fmcForFocusLoss);
+ }
+
+ /**
+ * see {@link AudioPolicy#clearFadeManagerConfigurationForFocusLoss()}
+ */
+ @android.annotation.EnforcePermission(
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public int clearFadeManagerConfigurationForFocusLoss() {
+ super.clearFadeManagerConfigurationForFocusLoss_enforcePermission();
+ ensureFadeManagerConfigIsEnabled();
+
+ return mPlaybackMonitor.clearFadeManagerConfiguration(AudioManager.AUDIOFOCUS_LOSS);
+ }
+
+ /**
+ * see {@link AudioPolicy#getFadeManagerConfigurationForFocusLoss()}
+ */
+ @android.annotation.EnforcePermission(
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss() {
+ super.getFadeManagerConfigurationForFocusLoss_enforcePermission();
+ ensureFadeManagerConfigIsEnabled();
+
+ return mPlaybackMonitor.getFadeManagerConfiguration(AudioManager.AUDIOFOCUS_LOSS);
+ }
+
+ /**
* @see AudioManager#getHalVersion
*/
public @Nullable AudioHalVersionInfo getHalVersion() {
for (AudioHalVersionInfo version : AudioHalVersionInfo.VERSIONS) {
try {
- // TODO: check AIDL service.
String versionStr = version.getMajorVersion() + "." + version.getMinorVersion();
- HwBinder.getService(
- String.format("android.hardware.audio@%s::IDevicesFactory", versionStr),
- "default");
- return version;
+ final String aidlStr = "android.hardware.audio.core.IModule/default";
+ final String hidlStr = String.format("android.hardware.audio@%s::IDevicesFactory",
+ versionStr);
+ if (null != ServiceManager.checkService(aidlStr)) {
+ return version;
+ } else {
+ HwBinder.getService(hidlStr, "default");
+ return version;
+ }
} catch (NoSuchElementException e) {
// Ignore, the specified HAL interface is not found.
} catch (RemoteException re) {
@@ -12814,6 +12883,19 @@
}
}
+ private void ensureFadeManagerConfigIsEnabled() {
+ Preconditions.checkState(enableFadeManagerConfiguration(),
+ "Fade manager configuration not supported");
+ }
+
+ private void validateFadeManagerConfiguration(FadeManagerConfiguration fmc) {
+ // validate permission of audio attributes
+ List<AudioAttributes> attrs = fmc.getAudioAttributesWithVolumeShaperConfigs();
+ for (int index = 0; index < attrs.size(); index++) {
+ validateAudioAttributesUsage(attrs.get(index));
+ }
+ }
+
//======================
// Audio policy callbacks from AudioSystem for dynamic policies
//======================
@@ -13114,6 +13196,7 @@
+ "could not link to " + projection + " binder death", e);
}
}
+
int status = connectMixes();
if (status != AudioSystem.SUCCESS) {
release();
@@ -13471,6 +13554,43 @@
}
}
+ /**
+ * see {@link AudioManager#dispatchAudioFocusChangeWithFade(AudioFocusInfo, int, AudioPolicy,
+ * List, FadeManagerConfiguration)}
+ */
+ @android.annotation.EnforcePermission(
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange,
+ IAudioPolicyCallback pcb, List<AudioFocusInfo> otherActiveAfis,
+ FadeManagerConfiguration transientFadeMgrConfig) {
+ super.dispatchFocusChangeWithFade_enforcePermission();
+ ensureFadeManagerConfigIsEnabled();
+ Objects.requireNonNull(afi, "AudioFocusInfo cannot be null");
+ Objects.requireNonNull(pcb, "AudioPolicy callback cannot be null");
+ Objects.requireNonNull(otherActiveAfis,
+ "Other active AudioFocusInfo list cannot be null");
+ if (transientFadeMgrConfig != null) {
+ validateFadeManagerConfiguration(transientFadeMgrConfig);
+ }
+
+ synchronized (mAudioPolicies) {
+ Preconditions.checkState(mAudioPolicies.containsKey(pcb.asBinder()),
+ "Unregistered AudioPolicy for focus dispatch with fade");
+
+ // set the transient fade manager config to be used for handling this focus change
+ if (transientFadeMgrConfig != null) {
+ mPlaybackMonitor.setTransientFadeManagerConfiguration(focusChange,
+ transientFadeMgrConfig);
+ }
+ int status = mMediaFocusControl.dispatchFocusChangeWithFade(afi, focusChange,
+ otherActiveAfis);
+
+ if (transientFadeMgrConfig != null) {
+ mPlaybackMonitor.clearTransientFadeManagerConfiguration(focusChange);
+ }
+ return status;
+ }
+ }
//======================
// Audioserver state dispatch
@@ -13775,8 +13895,8 @@
return activeAssistantUids;
}
- List<String> getDeviceAddresses(AudioDeviceAttributes device) {
- return mDeviceBroker.getDeviceAddresses(device);
+ List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) {
+ return mDeviceBroker.getDeviceIdentityAddresses(device);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 8075618..a818c30 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -58,6 +58,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.server.utils.EventLogger;
@@ -1077,8 +1078,8 @@
return mLeAudio.getGroupId(device);
}
- /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
- List<String> addresses = new ArrayList<String>();
+ /*package*/ List<Pair<String, String>> getLeAudioGroupAddresses(int groupId) {
+ List<Pair<String, String>> addresses = new ArrayList<>();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null || mLeAudio == null) {
return addresses;
@@ -1086,7 +1087,7 @@
List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
for (BluetoothDevice device : activeDevices) {
if (device != null && mLeAudio.getGroupId(device) == groupId) {
- addresses.add(device.getAddress());
+ addresses.add(new Pair(device.getAddress(), device.getIdentityAddress()));
}
}
return addresses;
diff --git a/services/core/java/com/android/server/audio/FadeConfigurations.java b/services/core/java/com/android/server/audio/FadeConfigurations.java
index 2e27c76..37ecf0b 100644
--- a/services/core/java/com/android/server/audio/FadeConfigurations.java
+++ b/services/core/java/com/android/server/audio/FadeConfigurations.java
@@ -16,13 +16,22 @@
package com.android.server.audio;
+import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
+import android.media.FadeManagerConfiguration;
import android.media.VolumeShaper;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Class to encapsulate configurations used for fading players
@@ -69,51 +78,229 @@
private static final int INVALID_UID = -1;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private FadeManagerConfiguration mDefaultFadeManagerConfig;
+ @GuardedBy("mLock")
+ private FadeManagerConfiguration mUpdatedFadeManagerConfig;
+ @GuardedBy("mLock")
+ private FadeManagerConfiguration mTransientFadeManagerConfig;
+ /** active fade manager is one of: transient > updated > default */
+ @GuardedBy("mLock")
+ private FadeManagerConfiguration mActiveFadeManagerConfig;
+
+ /**
+ * Sets the custom fade manager configuration
+ *
+ * @param fadeManagerConfig custom fade manager configuration
+ * @return {@link AudioManager#SUCCESS} if setting custom fade manager configuration succeeds
+ * or {@link AudioManager#ERROR} otherwise (example - when fade manager configuration
+ * feature is disabled)
+ */
+ public int setFadeManagerConfiguration(
+ @NonNull FadeManagerConfiguration fadeManagerConfig) {
+ if (!enableFadeManagerConfiguration()) {
+ return AudioManager.ERROR;
+ }
+
+ synchronized (mLock) {
+ mUpdatedFadeManagerConfig = Objects.requireNonNull(fadeManagerConfig,
+ "Fade manager configuration cannot be null");
+ mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked();
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ /**
+ * Clears the fade manager configuration that was previously set with
+ * {@link #setFadeManagerConfiguration(FadeManagerConfiguration)}
+ *
+ * @return {@link AudioManager#SUCCESS} if previously set fade manager configuration is cleared
+ * or {@link AudioManager#ERROR} otherwise (example, when fade manager configuration feature
+ * is disabled)
+ */
+ public int clearFadeManagerConfiguration() {
+ if (!enableFadeManagerConfiguration()) {
+ return AudioManager.ERROR;
+ }
+
+ synchronized (mLock) {
+ mUpdatedFadeManagerConfig = null;
+ mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked();
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ /**
+ * Returns the active fade manager configuration
+ *
+ * @return {@code null} if feature is disabled, or the custom fade manager configuration if set,
+ * or default fade manager configuration if not set.
+ */
+ @Nullable
+ public FadeManagerConfiguration getFadeManagerConfiguration() {
+ if (!enableFadeManagerConfiguration()) {
+ return null;
+ }
+
+ synchronized (mLock) {
+ return mActiveFadeManagerConfig;
+ }
+ }
+
+ /**
+ * Sets the transient fade manager configuration
+ *
+ * @param fadeManagerConfig custom fade manager configuration
+ * @return {@link AudioManager#SUCCESS} if setting custom fade manager configuration succeeds
+ * or {@link AudioManager#ERROR} otherwise (example - when fade manager configuration is
+ * disabled)
+ */
+ public int setTransientFadeManagerConfiguration(
+ @NonNull FadeManagerConfiguration fadeManagerConfig) {
+ if (!enableFadeManagerConfiguration()) {
+ return AudioManager.ERROR;
+ }
+
+ synchronized (mLock) {
+ mTransientFadeManagerConfig = Objects.requireNonNull(fadeManagerConfig,
+ "Transient FadeManagerConfiguration cannot be null");
+ mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked();
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ /**
+ * Clears the transient fade manager configuration that was previously set with
+ * {@link #setTransientFadeManagerConfiguration(FadeManagerConfiguration)}
+ *
+ * @return {@link AudioManager#SUCCESS} if previously set transient fade manager configuration
+ * is cleared or {@link AudioManager#ERROR} otherwise (example - when fade manager
+ * configuration is disabled)
+ */
+ public int clearTransientFadeManagerConfiguration() {
+ if (!enableFadeManagerConfiguration()) {
+ return AudioManager.ERROR;
+ }
+ synchronized (mLock) {
+ mTransientFadeManagerConfig = null;
+ mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked();
+ }
+ return AudioManager.SUCCESS;
+ }
+
+ /**
+ * Query if fade should be enforecd on players
+ *
+ * @return {@code true} if fade is enabled or using default configurations, {@code false}
+ * otherwise.
+ */
+ public boolean isFadeEnabled() {
+ if (!enableFadeManagerConfiguration()) {
+ return true;
+ }
+
+ synchronized (mLock) {
+ return getUpdatedFadeManagerConfigLocked().isFadeEnabled();
+ }
+ }
+
/**
* Query {@link android.media.AudioAttributes.AttributeUsage usages} that are allowed to
* fade
+ *
* @return list of {@link android.media.AudioAttributes.AttributeUsage}
*/
@NonNull
public List<Integer> getFadeableUsages() {
- return DEFAULT_FADEABLE_USAGES;
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_FADEABLE_USAGES;
+ }
+
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // when fade is not enabled, return an empty list instead
+ return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getFadeableUsages()
+ : Collections.EMPTY_LIST;
+ }
}
/**
* Query {@link android.media.AudioAttributes.AttributeContentType content types} that are
* exempted from fade enforcement
+ *
* @return list of {@link android.media.AudioAttributes.AttributeContentType}
*/
@NonNull
public List<Integer> getUnfadeableContentTypes() {
- return DEFAULT_UNFADEABLE_CONTENT_TYPES;
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_UNFADEABLE_CONTENT_TYPES;
+ }
+
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // when fade is not enabled, return an empty list instead
+ return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getUnfadeableContentTypes()
+ : Collections.EMPTY_LIST;
+ }
}
/**
* Query {@link android.media.AudioPlaybackConfiguration.PlayerType player types} that are
* exempted from fade enforcement
+ *
* @return list of {@link android.media.AudioPlaybackConfiguration.PlayerType}
*/
@NonNull
public List<Integer> getUnfadeablePlayerTypes() {
- return DEFAULT_UNFADEABLE_PLAYER_TYPES;
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_UNFADEABLE_PLAYER_TYPES;
+ }
+
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // when fade is not enabled, return an empty list instead
+ return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getUnfadeablePlayerTypes()
+ : Collections.EMPTY_LIST;
+ }
}
/**
* Get the {@link android.media.VolumeShaper.Configuration} configuration to be applied
* for the fade-out
+ *
* @param aa The {@link android.media.AudioAttributes}
* @return {@link android.media.VolumeShaper.Configuration} for the
- * {@link android.media.AudioAttributes.AttributeUsage} or default volume shaper if not
- * configured
+ * {@link android.media.AudioAttributes} or default volume shaper if not configured
*/
@NonNull
public VolumeShaper.Configuration getFadeOutVolumeShaperConfig(@NonNull AudioAttributes aa) {
- return DEFAULT_FADEOUT_VSHAPE;
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_FADEOUT_VSHAPE;
+ }
+ return getOptimalFadeOutVolShaperConfig(aa);
}
/**
+ * Get the {@link android.media.VolumeShaper.Configuration} configuration to be applied for the
+ * fade in
+ *
+ * @param aa The {@link android.media.AudioAttributes}
+ * @return {@link android.media.VolumeShaper.Configuration} for the
+ * {@link android.media.AudioAttributes} or {@code null} otherwise
+ */
+ @Nullable
+ public VolumeShaper.Configuration getFadeInVolumeShaperConfig(@NonNull AudioAttributes aa) {
+ if (!enableFadeManagerConfiguration()) {
+ return null;
+ }
+ return getOptimalFadeInVolShaperConfig(aa);
+ }
+
+
+ /**
* Get the duration to fade out a player of type usage
+ *
* @param aa The {@link android.media.AudioAttributes}
* @return duration in milliseconds for the
* {@link android.media.AudioAttributes} or default duration if not configured
@@ -122,22 +309,73 @@
if (!isFadeable(aa, INVALID_UID, AudioPlaybackConfiguration.PLAYER_TYPE_UNKNOWN)) {
return 0;
}
- return DEFAULT_FADE_OUT_DURATION_MS;
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_FADE_OUT_DURATION_MS;
+ }
+ return getOptimalFadeOutDuration(aa);
}
/**
- * Get the delay to fade in offending players that do not stop after losing audio focus.
+ * Get the delay to fade in offending players that do not stop after losing audio focus
+ *
* @param aa The {@link android.media.AudioAttributes}
* @return delay in milliseconds for the
* {@link android.media.AudioAttributes.Attribute} or default delay if not configured
*/
public long getDelayFadeInOffenders(@NonNull AudioAttributes aa) {
- return DEFAULT_DELAY_FADE_IN_OFFENDERS_MS;
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_DELAY_FADE_IN_OFFENDERS_MS;
+ }
+
+ synchronized (mLock) {
+ return getUpdatedFadeManagerConfigLocked().getFadeInDelayForOffenders();
+ }
+ }
+
+ /**
+ * Query {@link android.media.AudioAttributes} that are exempted from fade enforcement
+ *
+ * @return list of {@link android.media.AudioAttributes}
+ */
+ @NonNull
+ public List<AudioAttributes> getUnfadeableAudioAttributes() {
+ // unfadeable audio attributes is only supported with fade manager configurations
+ if (!enableFadeManagerConfiguration()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // when fade is not enabled, return empty list
+ return fadeManagerConfig.isFadeEnabled()
+ ? fadeManagerConfig.getUnfadeableAudioAttributes() : Collections.EMPTY_LIST;
+ }
+ }
+
+ /**
+ * Query uids that are exempted from fade enforcement
+ *
+ * @return list of uids
+ */
+ @NonNull
+ public List<Integer> getUnfadeableUids() {
+ // unfadeable uids is only supported with fade manager configurations
+ if (!enableFadeManagerConfiguration()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // when fade is not enabled, return empty list
+ return fadeManagerConfig.isFadeEnabled() ? fadeManagerConfig.getUnfadeableUids()
+ : Collections.EMPTY_LIST;
+ }
}
/**
* Check if it is allowed to fade for the given {@link android.media.AudioAttributes},
- * client uid and {@link android.media.AudioPlaybackConfiguration.PlayerType} config.
+ * client uid and {@link android.media.AudioPlaybackConfiguration.PlayerType} config
+ *
* @param aa The {@link android.media.AudioAttributes}
* @param uid The uid of the client owning the player
* @param playerType The {@link android.media.AudioPlaybackConfiguration.PlayerType}
@@ -145,36 +383,173 @@
*/
public boolean isFadeable(@NonNull AudioAttributes aa, int uid,
@AudioPlaybackConfiguration.PlayerType int playerType) {
- if (isPlayerTypeUnfadeable(playerType)) {
- if (DEBUG) {
- Slog.i(TAG, "not fadeable: player type:" + playerType);
+ synchronized (mLock) {
+ if (isPlayerTypeUnfadeableLocked(playerType)) {
+ if (DEBUG) {
+ Slog.i(TAG, "not fadeable: player type:" + playerType);
+ }
+ return false;
}
- return false;
- }
- if (isContentTypeUnfadeable(aa.getContentType())) {
- if (DEBUG) {
- Slog.i(TAG, "not fadeable: content type:" + aa.getContentType());
+ if (isContentTypeUnfadeableLocked(aa.getContentType())) {
+ if (DEBUG) {
+ Slog.i(TAG, "not fadeable: content type:" + aa.getContentType());
+ }
+ return false;
}
- return false;
- }
- if (!isUsageFadeable(aa.getUsage())) {
- if (DEBUG) {
- Slog.i(TAG, "not fadeable: usage:" + aa.getUsage());
+ if (!isUsageFadeableLocked(aa.getSystemUsage())) {
+ if (DEBUG) {
+ Slog.i(TAG, "not fadeable: usage:" + aa.getUsage());
+ }
+ return false;
}
- return false;
+ // new configs using fade manager configuration
+ if (isUnfadeableForFadeMgrConfigLocked(aa, uid)) {
+ return false;
+ }
+ return true;
}
- return true;
}
- private boolean isUsageFadeable(int usage) {
- return getFadeableUsages().contains(usage);
+ /** Tries to get the fade out volume shaper config closest to the audio attributes */
+ private VolumeShaper.Configuration getOptimalFadeOutVolShaperConfig(AudioAttributes aa) {
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // check if the specific audio attributes has a volume shaper config defined
+ VolumeShaper.Configuration volShaperConfig =
+ fadeManagerConfig.getFadeOutVolumeShaperConfigForAudioAttributes(aa);
+ if (volShaperConfig != null) {
+ return volShaperConfig;
+ }
+
+ // get the volume shaper config for usage
+ // for fadeable usages, this should never return null
+ return fadeManagerConfig.getFadeOutVolumeShaperConfigForUsage(
+ aa.getSystemUsage());
+ }
}
- private boolean isContentTypeUnfadeable(int contentType) {
- return getUnfadeableContentTypes().contains(contentType);
+ /** Tries to get the fade in volume shaper config closest to the audio attributes */
+ private VolumeShaper.Configuration getOptimalFadeInVolShaperConfig(AudioAttributes aa) {
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // check if the specific audio attributes has a volume shaper config defined
+ VolumeShaper.Configuration volShaperConfig =
+ fadeManagerConfig.getFadeInVolumeShaperConfigForAudioAttributes(aa);
+ if (volShaperConfig != null) {
+ return volShaperConfig;
+ }
+
+ // get the volume shaper config for usage
+ // for fadeable usages, this should never return null
+ return fadeManagerConfig.getFadeInVolumeShaperConfigForUsage(aa.getSystemUsage());
+ }
}
- private boolean isPlayerTypeUnfadeable(int playerType) {
- return getUnfadeablePlayerTypes().contains(playerType);
+ /** Tries to get the duration closest to the audio attributes */
+ private long getOptimalFadeOutDuration(AudioAttributes aa) {
+ synchronized (mLock) {
+ FadeManagerConfiguration fadeManagerConfig = getUpdatedFadeManagerConfigLocked();
+ // check if specific audio attributes has a duration defined
+ long duration = fadeManagerConfig.getFadeOutDurationForAudioAttributes(aa);
+ if (duration != FadeManagerConfiguration.DURATION_NOT_SET) {
+ return duration;
+ }
+
+ // get the duration for usage
+ // for fadeable usages, this should never return DURATION_NOT_SET
+ return fadeManagerConfig.getFadeOutDurationForUsage(aa.getSystemUsage());
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isUnfadeableForFadeMgrConfigLocked(AudioAttributes aa, int uid) {
+ if (isAudioAttributesUnfadeableLocked(aa)) {
+ if (DEBUG) {
+ Slog.i(TAG, "not fadeable: aa:" + aa);
+ }
+ return true;
+ }
+ if (isUidUnfadeableLocked(uid)) {
+ if (DEBUG) {
+ Slog.i(TAG, "not fadeable: uid:" + uid);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @GuardedBy("mLock")
+ private boolean isUsageFadeableLocked(int usage) {
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_FADEABLE_USAGES.contains(usage);
+ }
+ return getUpdatedFadeManagerConfigLocked().isUsageFadeable(usage);
+ }
+
+ @GuardedBy("mLock")
+ private boolean isContentTypeUnfadeableLocked(int contentType) {
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_UNFADEABLE_CONTENT_TYPES.contains(contentType);
+ }
+ return getUpdatedFadeManagerConfigLocked().isContentTypeUnfadeable(contentType);
+ }
+
+ @GuardedBy("mLock")
+ private boolean isPlayerTypeUnfadeableLocked(int playerType) {
+ if (!enableFadeManagerConfiguration()) {
+ return DEFAULT_UNFADEABLE_PLAYER_TYPES.contains(playerType);
+ }
+ return getUpdatedFadeManagerConfigLocked().isPlayerTypeUnfadeable(playerType);
+ }
+
+ @GuardedBy("mLock")
+ private boolean isAudioAttributesUnfadeableLocked(AudioAttributes aa) {
+ if (!enableFadeManagerConfiguration()) {
+ // default fade configs do not support unfadeable audio attributes, hence return false
+ return false;
+ }
+ return getUpdatedFadeManagerConfigLocked().isAudioAttributesUnfadeable(aa);
+ }
+
+ @GuardedBy("mLock")
+ private boolean isUidUnfadeableLocked(int uid) {
+ if (!enableFadeManagerConfiguration()) {
+ // default fade configs do not support unfadeable uids, hence return false
+ return false;
+ }
+ return getUpdatedFadeManagerConfigLocked().isUidUnfadeable(uid);
+ }
+
+ @GuardedBy("mLock")
+ private FadeManagerConfiguration getUpdatedFadeManagerConfigLocked() {
+ if (mActiveFadeManagerConfig == null) {
+ mActiveFadeManagerConfig = getActiveFadeMgrConfigLocked();
+ }
+ return mActiveFadeManagerConfig;
+ }
+
+ /** Priority between fade manager configs: Transient > Updated > Default */
+ @GuardedBy("mLock")
+ private FadeManagerConfiguration getActiveFadeMgrConfigLocked() {
+ // below configs are arranged in the order of priority
+ // configs placed higher have higher priority
+ if (mTransientFadeManagerConfig != null) {
+ return mTransientFadeManagerConfig;
+ }
+
+ if (mUpdatedFadeManagerConfig != null) {
+ return mUpdatedFadeManagerConfig;
+ }
+
+ // default - must be the lowest priority
+ return getDefaultFadeManagerConfigLocked();
+ }
+
+ @GuardedBy("mLock")
+ private FadeManagerConfiguration getDefaultFadeManagerConfigLocked() {
+ if (mDefaultFadeManagerConfig == null) {
+ mDefaultFadeManagerConfig = new FadeManagerConfiguration.Builder().build();
+ }
+ return mDefaultFadeManagerConfig;
}
}
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index 1171f97..cbcd8f5 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -16,21 +16,24 @@
package com.android.server.audio;
+import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
+import android.media.FadeManagerConfiguration;
import android.media.VolumeShaper;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Objects;
+import java.util.List;
+import java.util.Map;
/**
* Class to handle fading out players
@@ -40,14 +43,6 @@
public static final String TAG = "AS.FadeOutManager";
private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;
- private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
- new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
- .createIfNeeded()
- .build();
-
- // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp
- private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
- new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
private final Object mLock = new Object();
@@ -57,16 +52,81 @@
@GuardedBy("mLock")
private final SparseArray<FadedOutApp> mUidToFadedAppsMap = new SparseArray<>();
- @GuardedBy("mLock")
- private FadeConfigurations mFadeConfigurations;
+ private final FadeConfigurations mFadeConfigurations = new FadeConfigurations();
- public FadeOutManager() {
- mFadeConfigurations = new FadeConfigurations();
+ /**
+ * Sets the custom fade manager configuration to be used for player fade out and in
+ *
+ * @param fadeManagerConfig custom fade manager configuration
+ * @return {@link AudioManager#SUCCESS} if setting fade manager config succeeded,
+ * {@link AudioManager#ERROR} otherwise
+ */
+ int setFadeManagerConfiguration(FadeManagerConfiguration fadeManagerConfig) {
+ // locked to ensure the fade configs are not updated while faded app state is being updated
+ synchronized (mLock) {
+ return mFadeConfigurations.setFadeManagerConfiguration(fadeManagerConfig);
+ }
}
- public FadeOutManager(FadeConfigurations fadeConfigurations) {
- mFadeConfigurations = Objects.requireNonNull(fadeConfigurations,
- "Fade configurations can not be null");
+ /**
+ * Clears the fade manager configuration that was previously set with
+ * {@link #setFadeManagerConfiguration(FadeManagerConfiguration)}
+ *
+ * @return {@link AudioManager#SUCCESS} if clearing fade manager config succeeded,
+ * {@link AudioManager#ERROR} otherwise
+ */
+ int clearFadeManagerConfiguration() {
+ // locked to ensure the fade configs are not updated while faded app state is being updated
+ synchronized (mLock) {
+ return mFadeConfigurations.clearFadeManagerConfiguration();
+ }
+ }
+
+ /**
+ * Returns the active fade manager configuration
+ *
+ * @return the {@link FadeManagerConfiguration}
+ */
+ FadeManagerConfiguration getFadeManagerConfiguration() {
+ return mFadeConfigurations.getFadeManagerConfiguration();
+ }
+
+ /**
+ * Sets the transient fade manager configuration to be used for player fade out and in
+ *
+ * @param fadeManagerConfig fade manager config that has higher priority than the existing
+ * fade manager configuration. This is expected to be transient.
+ * @return {@link AudioManager#SUCCESS} if setting fade manager config succeeded,
+ * {@link AudioManager#ERROR} otherwise
+ */
+ int setTransientFadeManagerConfiguration(FadeManagerConfiguration fadeManagerConfig) {
+ // locked to ensure the fade configs are not updated while faded app state is being updated
+ synchronized (mLock) {
+ return mFadeConfigurations.setTransientFadeManagerConfiguration(fadeManagerConfig);
+ }
+ }
+
+ /**
+ * Clears the transient fade manager configuration that was previously set with
+ * {@link #setTransientFadeManagerConfiguration(FadeManagerConfiguration)}
+ *
+ * @return {@link AudioManager#SUCCESS} if clearing fade manager config succeeded,
+ * {@link AudioManager#ERROR} otherwise
+ */
+ int clearTransientFadeManagerConfiguration() {
+ // locked to ensure the fade configs are not updated while faded app state is being updated
+ synchronized (mLock) {
+ return mFadeConfigurations.clearTransientFadeManagerConfiguration();
+ }
+ }
+
+ /**
+ * Query if fade is enblead and can be enforced on players
+ *
+ * @return {@code true} if fade is enabled, {@code false} otherwise.
+ */
+ boolean isFadeEnabled() {
+ return mFadeConfigurations.isFadeEnabled();
}
// TODO explore whether a shorter fade out would be a better UX instead of not fading out at all
@@ -128,7 +188,7 @@
}
}
- void fadeOutUid(int uid, ArrayList<AudioPlaybackConfiguration> players) {
+ void fadeOutUid(int uid, List<AudioPlaybackConfiguration> players) {
Slog.i(TAG, "fadeOutUid() uid:" + uid);
synchronized (mLock) {
if (!mUidToFadedAppsMap.contains(uid)) {
@@ -148,15 +208,31 @@
* @param uid the uid for the app to unfade out
* @param players map of current available players (so we can get an APC from piid)
*/
- void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
+ void unfadeOutUid(int uid, Map<Integer, AudioPlaybackConfiguration> players) {
Slog.i(TAG, "unfadeOutUid() uid:" + uid);
synchronized (mLock) {
- final FadedOutApp fa = mUidToFadedAppsMap.get(uid);
+ FadedOutApp fa = mUidToFadedAppsMap.get(uid);
if (fa == null) {
return;
}
mUidToFadedAppsMap.remove(uid);
- fa.removeUnfadeAll(players);
+
+ if (!enableFadeManagerConfiguration()) {
+ fa.removeUnfadeAll(players);
+ return;
+ }
+
+ // since fade manager configs may have volume-shaper config per audio attributes,
+ // iterate through each palyer and gather respective configs for fade in
+ ArrayList<AudioPlaybackConfiguration> apcs = new ArrayList<>(players.values());
+ for (int index = 0; index < apcs.size(); index++) {
+ AudioPlaybackConfiguration apc = apcs.get(index);
+ VolumeShaper.Configuration config = mFadeConfigurations
+ .getFadeInVolumeShaperConfig(apc.getAudioAttributes());
+ fa.fadeInPlayer(apc, config);
+ }
+ // ideal case all players should be faded in
+ fa.clear();
}
}
@@ -209,16 +285,6 @@
}
}
- /**
- * Update fade configurations used for player fade operations
- * @param fadeConfigurations set of configs that define fade properties
- */
- void setFadeConfigurations(@NonNull FadeConfigurations fadeConfigurations) {
- synchronized (mLock) {
- mFadeConfigurations = fadeConfigurations;
- }
- }
-
void dump(PrintWriter pw) {
synchronized (mLock) {
for (int index = 0; index < mUidToFadedAppsMap.size(); index++) {
@@ -232,6 +298,15 @@
* Class to group players from a common app, that are faded out.
*/
private static final class FadedOutApp {
+ private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
+ new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
+ .createIfNeeded()
+ .build();
+
+ // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp
+ private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
+ new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
+
private final int mUid;
// key -> piid; value -> volume shaper config applied
private final SparseArray<VolumeShaper.Configuration> mFadedPlayers = new SparseArray<>();
@@ -269,17 +344,9 @@
return;
}
if (apc.getPlayerProxy() != null) {
- try {
- PlaybackActivityMonitor.sEventLogger.enqueue(
- (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp)).printLog(
- TAG));
- apc.getPlayerProxy().applyVolumeShaper(volShaper,
- skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
- mFadedPlayers.put(piid, volShaper);
- } catch (Exception e) {
- Slog.e(TAG, "Error fading out player piid:" + piid
- + " uid:" + apc.getClientUid(), e);
- }
+ applyVolumeShaperInternal(apc, piid, volShaper,
+ skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
+ mFadedPlayers.put(piid, volShaper);
} else {
if (DEBUG) {
Slog.v(TAG, "Error fading out player piid:" + piid
@@ -288,21 +355,13 @@
}
}
- void removeUnfadeAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
+ void removeUnfadeAll(Map<Integer, AudioPlaybackConfiguration> players) {
for (int index = 0; index < mFadedPlayers.size(); index++) {
int piid = mFadedPlayers.keyAt(index);
final AudioPlaybackConfiguration apc = players.get(piid);
if ((apc != null) && (apc.getPlayerProxy() != null)) {
- final VolumeShaper.Configuration volShaper = mFadedPlayers.valueAt(index);
- try {
- PlaybackActivityMonitor.sEventLogger.enqueue(
- (new EventLogger.StringEvent("unfading out piid:"
- + piid)).printLog(TAG));
- apc.getPlayerProxy().applyVolumeShaper(volShaper,
- VolumeShaper.Operation.REVERSE);
- } catch (Exception e) {
- Slog.e(TAG, "Error unfading out player piid:" + piid + " uid:" + mUid, e);
- }
+ applyVolumeShaperInternal(apc, piid, /* volShaperConfig= */ null,
+ VolumeShaper.Operation.REVERSE);
} else {
// this piid was in the list of faded players, but wasn't found
if (DEBUG) {
@@ -314,8 +373,61 @@
mFadedPlayers.clear();
}
+ void fadeInPlayer(@NonNull AudioPlaybackConfiguration apc,
+ @Nullable VolumeShaper.Configuration config) {
+ int piid = Integer.valueOf(apc.getPlayerInterfaceId());
+ // if not found, no need to fade in since it was never faded out
+ if (!mFadedPlayers.contains(piid)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Player (piid: " + piid + ") for uid (" + mUid
+ + ") is not faded out, no need to fade in");
+ }
+ return;
+ }
+
+ mFadedPlayers.remove(piid);
+ if (apc.getPlayerProxy() != null) {
+ applyVolumeShaperInternal(apc, piid, config,
+ config != null ? PLAY_CREATE_IF_NEEDED : VolumeShaper.Operation.REVERSE);
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "Error fading in player piid:" + piid
+ + ", player not found for uid " + mUid);
+ }
+ }
+ }
+
+ void clear() {
+ if (mFadedPlayers.size() > 0) {
+ if (DEBUG) {
+ Slog.v(TAG, "Non empty faded players list being cleared! Faded out players:"
+ + mFadedPlayers);
+ }
+ }
+ // should the players be faded in irrespective?
+ mFadedPlayers.clear();
+ }
+
void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
mFadedPlayers.delete(Integer.valueOf(apc.getPlayerInterfaceId()));
}
+
+ private void applyVolumeShaperInternal(AudioPlaybackConfiguration apc, int piid,
+ VolumeShaper.Configuration volShaperConfig, VolumeShaper.Operation operation) {
+ VolumeShaper.Configuration config = volShaperConfig;
+ // when operation is reverse, use the fade out volume shaper config instead
+ if (operation.equals(VolumeShaper.Operation.REVERSE)) {
+ config = mFadedPlayers.get(piid);
+ }
+ try {
+ PlaybackActivityMonitor.sEventLogger.enqueue(
+ (new PlaybackActivityMonitor.FadeEvent(apc, config, operation))
+ .printLog(TAG));
+ apc.getPlayerProxy().applyVolumeShaper(config, operation);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error fading player piid:" + piid + " uid:" + mUid
+ + " operation:" + operation, e);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 00c04ff..f462539 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -33,6 +33,7 @@
import com.android.server.pm.UserManagerInternal;
import java.io.PrintWriter;
+import java.util.List;
/**
* @hide
@@ -534,6 +535,33 @@
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
+ @GuardedBy("MediaFocusControl.mAudioFocusLock")
+ int dispatchFocusChangeWithFadeLocked(int focusChange, List<FocusRequester> otherActiveFrs) {
+ if (focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
+ || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
+ || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
+ || focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ mFocusLossFadeLimbo = false;
+ mFocusController.restoreVShapedPlayers(this);
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS
+ && mFocusController.shouldEnforceFade()) {
+ for (int index = 0; index < otherActiveFrs.size(); index++) {
+ // candidate for fade-out before a receiving a loss
+ if (mFocusController.fadeOutPlayers(otherActiveFrs.get(index), /* loser= */ this)) {
+ // active players are being faded out, delay the dispatch of focus loss
+ // mark this instance as being faded so it's not released yet as the focus loss
+ // will be dispatched later, it is now in limbo mode
+ mFocusLossFadeLimbo = true;
+ mFocusController.postDelayedLossAfterFade(this,
+ mFocusController.getFadeOutDurationOnFocusLossMillis(
+ this.getAudioAttributes()));
+ return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
+ }
+ }
+ }
+ return dispatchFocusChange(focusChange);
+ }
+
void dispatchFocusResultFromExtPolicy(int requestResult) {
final IAudioFocusDispatcher fd = mFocusDispatcher;
if (fd == null) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 58f5d5e..0df0006 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -16,6 +16,8 @@
package com.android.server.audio;
+import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -195,6 +197,15 @@
}
return mFocusEnforcer.getFadeInDelayForOffendersMillis(aa);
}
+
+ @Override
+ public boolean shouldEnforceFade() {
+ if (!enableFadeManagerConfiguration()) {
+ return ENFORCE_FADEOUT_FOR_FOCUS_LOSS;
+ }
+
+ return mFocusEnforcer.shouldEnforceFade();
+ }
//==========================================================================================
// AudioFocus
//==========================================================================================
@@ -861,14 +872,17 @@
return;
}
}
- final FocusRequester fr;
- if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
- fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
- } else {
- fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
- }
- if (fr != null) {
- fr.dispatchFocusResultFromExtPolicy(requestResult);
+ synchronized (mAudioFocusLock) {
+ FocusRequester fr = getFocusRequesterLocked(afi.getClientId(),
+ /* shouldRemove= */ requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED);
+ if (fr != null) {
+ fr.dispatchFocusResultFromExtPolicy(requestResult);
+ // if fade is enabled for external focus policies, apply it when setting
+ // focus result as well
+ if (enableFadeManagerConfiguration()) {
+ fr.handleFocusGainFromRequest(requestResult);
+ }
+ }
}
}
@@ -902,24 +916,80 @@
+ afi.getClientId());
}
synchronized (mAudioFocusLock) {
- if (mFocusPolicy == null) {
- if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); }
- return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
- }
- final FocusRequester fr;
- if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
- fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
- } else {
- fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
- }
+ FocusRequester fr = getFocusRequesterLocked(afi.getClientId(),
+ /* shouldRemove= */ focusChange == AudioManager.AUDIOFOCUS_LOSS);
if (fr == null) {
- if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); }
+ if (DEBUG) {
+ Log.v(TAG, "> failed: no such focus requester known");
+ }
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
return fr.dispatchFocusChange(focusChange);
}
}
+ int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange,
+ List<AudioFocusInfo> otherActiveAfis) {
+ if (DEBUG) {
+ Log.v(TAG, "dispatchFocusChangeWithFade " + AudioManager.audioFocusToString(focusChange)
+ + " to afi client=" + afi.getClientId()
+ + " other active afis=" + otherActiveAfis);
+ }
+
+ synchronized (mAudioFocusLock) {
+ String clientId = afi.getClientId();
+ // do not remove the entry since it can be posted for fade
+ FocusRequester fr = getFocusRequesterLocked(clientId, /* shouldRemove= */ false);
+ if (fr == null) {
+ if (DEBUG) {
+ Log.v(TAG, "> failed: no such focus requester known");
+ }
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
+ // convert other AudioFocusInfo to corresponding FocusRequester
+ ArrayList<FocusRequester> otherActiveFrs = new ArrayList<>();
+ for (int index = 0; index < otherActiveAfis.size(); index++) {
+ FocusRequester otherFr = getFocusRequesterLocked(
+ otherActiveAfis.get(index).getClientId(), /* shouldRemove= */ false);
+ if (otherFr == null) {
+ continue;
+ }
+ otherActiveFrs.add(otherFr);
+ }
+
+ int status = fr.dispatchFocusChangeWithFadeLocked(focusChange, otherActiveFrs);
+ if (status != AudioManager.AUDIOFOCUS_REQUEST_DELAYED
+ && focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+ mFocusOwnersForFocusPolicy.remove(clientId);
+ }
+
+ return status;
+ }
+ }
+
+ @GuardedBy("mAudioFocusLock")
+ private FocusRequester getFocusRequesterLocked(String clientId, boolean shouldRemove) {
+ if (mFocusPolicy == null) {
+ if (DEBUG) {
+ Log.v(TAG, "> failed: no focus policy");
+ }
+ return null;
+ }
+
+ FocusRequester fr;
+ if (shouldRemove) {
+ fr = mFocusOwnersForFocusPolicy.remove(clientId);
+ } else {
+ fr = mFocusOwnersForFocusPolicy.get(clientId);
+ }
+
+ if (fr == null && DEBUG) {
+ Log.v(TAG, "> failed: no such focus requester known");
+ }
+ return fr;
+ }
+
private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) {
final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index bc9b9b4..e69fbbd 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -38,6 +38,7 @@
import android.media.AudioPlaybackConfiguration.FormatInfo;
import android.media.AudioPlaybackConfiguration.PlayerMuteEvent;
import android.media.AudioSystem;
+import android.media.FadeManagerConfiguration;
import android.media.IPlaybackConfigDispatcher;
import android.media.PlayerBase;
import android.media.VolumeShaper;
@@ -156,8 +157,7 @@
private final int mMaxAlarmVolume;
private int mPrivilegedAlarmActiveCount = 0;
private final Consumer<AudioDeviceAttributes> mMuteAwaitConnectionTimeoutCb;
- private final FadeOutManager mFadeOutManager;
-
+ private final FadeOutManager mFadeOutManager = new FadeOutManager();
PlaybackActivityMonitor(Context context, int maxAlarmVolume,
Consumer<AudioDeviceAttributes> muteTimeoutCallback) {
@@ -167,7 +167,6 @@
AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
mMuteAwaitConnectionTimeoutCb = muteTimeoutCallback;
initEventHandler();
- mFadeOutManager = new FadeOutManager(new FadeConfigurations());
}
//=================================================================
@@ -971,6 +970,12 @@
return mFadeOutManager.getFadeInDelayForOffendersMillis(aa);
}
+ @Override
+ public boolean shouldEnforceFade() {
+ return mFadeOutManager.isFadeEnabled();
+ }
+
+
//=================================================================
// Track playback activity listeners
@@ -1010,6 +1015,27 @@
}
}
+ int setFadeManagerConfiguration(int focusType, FadeManagerConfiguration fadeMgrConfig) {
+ return mFadeOutManager.setFadeManagerConfiguration(fadeMgrConfig);
+ }
+
+ int clearFadeManagerConfiguration(int focusType) {
+ return mFadeOutManager.clearFadeManagerConfiguration();
+ }
+
+ FadeManagerConfiguration getFadeManagerConfiguration(int focusType) {
+ return mFadeOutManager.getFadeManagerConfiguration();
+ }
+
+ int setTransientFadeManagerConfiguration(int focusType,
+ FadeManagerConfiguration fadeMgrConfig) {
+ return mFadeOutManager.setTransientFadeManagerConfiguration(fadeMgrConfig);
+ }
+
+ int clearTransientFadeManagerConfiguration(int focusType) {
+ return mFadeOutManager.clearTransientFadeManagerConfiguration();
+ }
+
/**
* Inner class to track clients that want to be notified of playback updates
*/
@@ -1337,6 +1363,38 @@
}
}
+ static final class FadeEvent extends EventLogger.Event {
+ private final int mPlayerIId;
+ private final int mPlayerType;
+ private final int mClientUid;
+ private final int mClientPid;
+ private final AudioAttributes mPlayerAttr;
+ private final VolumeShaper.Configuration mVShaper;
+ private final VolumeShaper.Operation mVOperation;
+
+ FadeEvent(AudioPlaybackConfiguration apc, VolumeShaper.Configuration vShaper,
+ VolumeShaper.Operation vOperation) {
+ mPlayerIId = apc.getPlayerInterfaceId();
+ mClientUid = apc.getClientUid();
+ mClientPid = apc.getClientPid();
+ mPlayerAttr = apc.getAudioAttributes();
+ mPlayerType = apc.getPlayerType();
+ mVShaper = vShaper;
+ mVOperation = vOperation;
+ }
+
+ @Override
+ public String eventToString() {
+ return "Fade Event:" + " player piid:" + mPlayerIId
+ + " uid/pid:" + mClientUid + "/" + mClientPid
+ + " player type:"
+ + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
+ + " attr:" + mPlayerAttr
+ + " volume shaper:" + mVShaper
+ + " volume operation:" + mVOperation;
+ }
+ }
+
private abstract static class VolumeShaperEvent extends EventLogger.Event {
private final int mPlayerIId;
private final boolean mSkipRamp;
diff --git a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
index f1d42f3..4a29eca 100644
--- a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
+++ b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
@@ -79,4 +79,11 @@
* @return delay in milliseconds
*/
long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa);
+
+ /**
+ * Check if the fade should be enforced
+ *
+ * @return {@code true} if fade should be enforced, {@code false} otherwise
+ */
+ boolean shouldEnforceFade();
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 4f7f31d..8428f12 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -1639,8 +1639,7 @@
return -1;
}
final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
- List<String> deviceAddresses = mAudioService.getDeviceAddresses(currentDevice);
-
+ List<String> deviceAddresses = mAudioService.getDeviceIdentityAddresses(currentDevice);
// We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion
// with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR
// and Sensor.TYPE_GAME_ROTATION_VECTOR are supported internally by
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 4df2581..5d609bc 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -54,8 +54,8 @@
@NonNull private final Context mContext;
@NonNull private final PackageManager mPackageManager;
- @NonNull private final FaceManager mFaceManager;
- @NonNull private final FingerprintManager mFingerprintManager;
+ @Nullable private final FaceManager mFaceManager;
+ @Nullable private final FingerprintManager mFingerprintManager;
private final boolean mEnabled;
private final float mThreshold;
@@ -197,11 +197,11 @@
}
private boolean hasEnrolledFace(int userId) {
- return mFaceManager.hasEnrolledTemplates(userId);
+ return mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId);
}
private boolean hasEnrolledFingerprint(int userId) {
- return mFingerprintManager.hasEnrolledTemplates(userId);
+ return mFingerprintManager != null && mFingerprintManager.hasEnrolledTemplates(userId);
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 6af223b..0f964bb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -78,6 +78,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.function.Function;
import java.util.function.Supplier;
/**
@@ -99,6 +100,9 @@
mBiometricStateCallback;
@NonNull
private final FaceProviderFunction mFaceProviderFunction;
+ @NonNull private final Function<String, FaceProvider> mFaceProvider;
+ @NonNull
+ private final Supplier<String[]> mAidlInstanceNameSupplier;
interface FaceProviderFunction {
FaceProvider getFaceProvider(Pair<String, SensorProps[]> filteredSensorProps,
@@ -671,23 +675,9 @@
final List<ServiceProvider> providers = new ArrayList<>();
for (String instance : instances) {
- final String fqName = IFace.DESCRIPTOR + "/" + instance;
- final IFace face = IFace.Stub.asInterface(
- Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
- if (face == null) {
- Slog.e(TAG, "Unable to get declared service: " + fqName);
- continue;
- }
- try {
- final SensorProps[] props = face.getSensorProps();
- final FaceProvider provider = new FaceProvider(getContext(),
- mBiometricStateCallback, props, instance, mLockoutResetDispatcher,
- BiometricContext.getInstance(getContext()),
- false /* resetLockoutRequiresChallenge */);
- providers.add(provider);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
- }
+ final FaceProvider provider = mFaceProvider.apply(instance);
+ Slog.i(TAG, "Adding AIDL provider: " + instance);
+ providers.add(provider);
}
return providers;
@@ -700,7 +690,7 @@
mRegistry.registerAll(() -> {
List<String> aidlSensors = new ArrayList<>();
- final String[] instances = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+ final String[] instances = mAidlInstanceNameSupplier.get();
if (instances != null) {
aidlSensors.addAll(Lists.newArrayList(instances));
}
@@ -813,11 +803,15 @@
public FaceService(Context context) {
this(context, null /* faceProviderFunction */, () -> IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)), null /* faceProvider */,
+ () -> ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR));
}
- @VisibleForTesting FaceService(Context context, FaceProviderFunction faceProviderFunction,
- Supplier<IBiometricService> biometricServiceSupplier) {
+ @VisibleForTesting FaceService(Context context,
+ FaceProviderFunction faceProviderFunction,
+ Supplier<IBiometricService> biometricServiceSupplier,
+ Function<String, FaceProvider> faceProvider,
+ Supplier<String[]> aidlInstanceNameSupplier) {
super(context);
mServiceWrapper = new FaceServiceWrapper();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
@@ -830,6 +824,28 @@
mBiometricStateCallback.start(mRegistry.getProviders());
}
});
+ mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
+
+ mFaceProvider = faceProvider != null ? faceProvider : (name) -> {
+ final String fqName = IFace.DESCRIPTOR + "/" + name;
+ final IFace face = IFace.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ if (face == null) {
+ Slog.e(TAG, "Unable to get declared service: " + fqName);
+ return null;
+ }
+ try {
+ final SensorProps[] props = face.getSensorProps();
+ return new FaceProvider(getContext(),
+ mBiometricStateCallback, props, name, mLockoutResetDispatcher,
+ BiometricContext.getInstance(getContext()),
+ false /* resetLockoutRequiresChallenge */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
+ }
+
+ return null;
+ };
if (Flags.deHidl()) {
mFaceProviderFunction = faceProviderFunction != null ? faceProviderFunction :
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 2314bb7..8910b6e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -72,12 +74,15 @@
@IntDef(prefix = { "AUTO_BRIGHTNESS_MODE_" }, value = {
AUTO_BRIGHTNESS_MODE_DEFAULT,
AUTO_BRIGHTNESS_MODE_IDLE,
+ AUTO_BRIGHTNESS_MODE_DOZE
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutomaticBrightnessMode{}
public static final int AUTO_BRIGHTNESS_MODE_DEFAULT = 0;
public static final int AUTO_BRIGHTNESS_MODE_IDLE = 1;
+ public static final int AUTO_BRIGHTNESS_MODE_DOZE = 2;
+ public static final int AUTO_BRIGHTNESS_MODE_MAX = AUTO_BRIGHTNESS_MODE_DOZE;
// How long the current sensor reading is assumed to be valid beyond the current time.
// This provides a bit of prediction, as well as ensures that the weight for the last sample is
@@ -616,12 +621,13 @@
pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
- pw.println(" Current mode=" + mCurrentBrightnessMapper.getMode());
+ pw.println(" Current mode="
+ + autoBrightnessModeToString(mCurrentBrightnessMapper.getMode()));
pw.println();
for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) {
- pw.println(" Mapper for mode " + modeToString(mBrightnessMappingStrategyMap.keyAt(i))
- + "=");
+ pw.println(" Mapper for mode "
+ + autoBrightnessModeToString(mBrightnessMappingStrategyMap.keyAt(i)) + "=");
mBrightnessMappingStrategyMap.valueAt(i).dump(pw,
mBrightnessRangeController.getNormalBrightnessMax());
}
@@ -1224,14 +1230,6 @@
}
}
- private String modeToString(@AutomaticBrightnessMode int mode) {
- return switch (mode) {
- case AUTO_BRIGHTNESS_MODE_DEFAULT -> "default";
- case AUTO_BRIGHTNESS_MODE_IDLE -> "idle";
- default -> Integer.toString(mode);
- };
- }
-
private class ShortTermModel {
// When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
// user's adjustment) immediately, but wait for a drastic enough change in the ambient
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index acd253b..8405e0a 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -19,15 +19,18 @@
import static android.text.TextUtils.formatSimple;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessCorrection;
import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.LongArray;
import android.util.MathUtils;
import android.util.Pair;
@@ -35,7 +38,6 @@
import android.util.Spline;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.display.BrightnessUtils;
import com.android.internal.util.Preconditions;
import com.android.server.display.utils.Plog;
@@ -80,52 +82,50 @@
* Creates a BrightnessMapping strategy. We do not create a simple mapping strategy for idle
* mode.
*
- * @param resources
+ * @param context
* @param displayDeviceConfig
* @param mode The auto-brightness mode. Different modes use different brightness curves
* @param displayWhiteBalanceController
* @return the BrightnessMappingStrategy
*/
@Nullable
- static BrightnessMappingStrategy create(Resources resources,
+ static BrightnessMappingStrategy create(Context context,
DisplayDeviceConfig displayDeviceConfig,
@AutomaticBrightnessController.AutomaticBrightnessMode int mode,
- DisplayWhiteBalanceController displayWhiteBalanceController) {
+ @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) {
// Display independent, mode dependent values
float[] brightnessLevelsNits = null;
float[] brightnessLevels = null;
float[] luxLevels = null;
+ int preset = Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
switch (mode) {
- case AUTO_BRIGHTNESS_MODE_DEFAULT:
+ case AUTO_BRIGHTNESS_MODE_DEFAULT -> {
brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
- luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux();
-
- brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels();
- if (brightnessLevels == null || brightnessLevels.length == 0) {
- // Load the old configuration in the range [0, 255]. The values need to be
- // normalized to the range [0, 1].
- int[] brightnessLevelsInt = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
- brightnessLevels = new float[brightnessLevelsInt.length];
- for (int i = 0; i < brightnessLevels.length; i++) {
- brightnessLevels[i] = normalizeAbsoluteBrightness(brightnessLevelsInt[i]);
- }
- }
- break;
- case AUTO_BRIGHTNESS_MODE_IDLE:
- brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
+ luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
+ brightnessLevels =
+ displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
+ }
+ case AUTO_BRIGHTNESS_MODE_IDLE -> {
+ brightnessLevelsNits = getFloatArray(context.getResources().obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle));
- luxLevels = getLuxLevels(resources.getIntArray(
+ luxLevels = getLuxLevels(context.getResources().getIntArray(
com.android.internal.R.array.config_autoBrightnessLevelsIdle));
- break;
+ }
+ case AUTO_BRIGHTNESS_MODE_DOZE -> {
+ luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
+ brightnessLevels =
+ displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
+ }
}
// Display independent, mode independent values
- float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
+ float autoBrightnessAdjustmentMaxGamma = context.getResources().getFraction(
com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
1, 1);
- long shortTermModelTimeout = resources.getInteger(
+ long shortTermModelTimeout = context.getResources().getInteger(
com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
// Display dependent values - used for physical mapping strategy nits -> brightness
@@ -426,11 +426,6 @@
}
}
- // Normalize entire brightness range to 0 - 1.
- protected static float normalizeAbsoluteBrightness(int brightness) {
- return BrightnessSynchronizer.brightnessIntToFloat(brightness);
- }
-
private Pair<float[], float[]> insertControlPoint(
float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
final int idx = findInsertionPoint(luxLevels, lux);
@@ -835,6 +830,8 @@
private float mAutoBrightnessAdjustment;
private float mUserLux;
private float mUserBrightness;
+
+ @Nullable
private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
@AutomaticBrightnessController.AutomaticBrightnessMode
@@ -850,7 +847,7 @@
public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
float[] brightness, float maxGamma,
@AutomaticBrightnessController.AutomaticBrightnessMode int mode,
- DisplayWhiteBalanceController displayWhiteBalanceController) {
+ @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) {
Preconditions.checkArgument(nits.length != 0 && brightness.length != 0,
"Nits and brightness arrays must not be empty!");
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index d97127c..bd22e1d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -49,6 +49,7 @@
import com.android.server.display.config.BrightnessThrottlingMap;
import com.android.server.display.config.BrightnessThrottlingPoint;
import com.android.server.display.config.Density;
+import com.android.server.display.config.DisplayBrightnessMappingConfig;
import com.android.server.display.config.DisplayBrightnessPoint;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
@@ -57,7 +58,6 @@
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.LuxThrottling;
-import com.android.server.display.config.LuxToBrightnessMapping;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.NonNegativeFloatToFloatPoint;
import com.android.server.display.config.Point;
@@ -313,6 +313,21 @@
* 1000
* </darkeningLightDebounceIdleMillis>
* <luxToBrightnessMapping>
+ * <mode>default</mode>
+ * <map>
+ * <point>
+ * <first>0</first>
+ * <second>0.2</second>
+ * </point>
+ * <point>
+ * <first>80</first>
+ * <second>0.3</second>
+ * </point>
+ * </map>
+ * </luxToBrightnessMapping>
+ * <luxToBrightnessMapping>
+ * <mode>doze</mode>
+ * <setting>dim</setting>
* <map>
* <point>
* <first>0</first>
@@ -634,36 +649,8 @@
// for the corresponding values above
private float[] mBrightness;
- /**
- * Array of desired screen brightness in nits corresponding to the lux values
- * in the mBrightnessLevelsLux array. The display brightness is defined as the
- * measured brightness of an all-white image. The brightness values must be non-negative and
- * non-decreasing. This must be overridden in platform specific overlays
- */
- private float[] mBrightnessLevelsNits;
-
- /**
- * Array of desired screen brightness corresponding to the lux values
- * in the mBrightnessLevelsLux array. The brightness values must be non-negative and
- * non-decreasing. They must be between {@link PowerManager.BRIGHTNESS_MIN} and
- * {@link PowerManager.BRIGHTNESS_MAX}. This must be overridden in platform specific overlays
- */
- private float[] mBrightnessLevels;
-
- /**
- * Array of light sensor lux values to define our levels for auto-brightness support.
- *
- * The first lux value is always 0.
- *
- * The control points must be strictly increasing. Each control point corresponds to an entry
- * in the brightness values arrays. For example, if lux == luxLevels[1] (second element
- * of the levels array) then the brightness will be determined by brightnessLevels[1] (second
- * element of the brightness values array).
- *
- * Spline interpolation is used to determine the auto-brightness values for lux levels between
- * these control points.
- */
- private float[] mBrightnessLevelsLux;
+ @Nullable
+ private DisplayBrightnessMappingConfig mDisplayBrightnessMapping;
private float mBacklightMinimum = Float.NaN;
private float mBacklightMaximum = Float.NaN;
@@ -1604,24 +1591,42 @@
}
/**
- * @return Auto brightness brightening ambient lux levels
+ * @param mode The auto-brightness mode
+ * @param preset The brightness preset. Presets are used on devices that allow users to choose
+ * from a set of predefined options in display auto-brightness settings.
+ * @return The default auto-brightness brightening ambient lux levels for the specified mode
+ * and preset
*/
- public float[] getAutoBrightnessBrighteningLevelsLux() {
- return mBrightnessLevelsLux;
+ public float[] getAutoBrightnessBrighteningLevelsLux(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) {
+ if (mDisplayBrightnessMapping == null) {
+ return null;
+ }
+ return mDisplayBrightnessMapping.getLuxArray(mode, preset);
}
/**
* @return Auto brightness brightening nits levels
*/
public float[] getAutoBrightnessBrighteningLevelsNits() {
- return mBrightnessLevelsNits;
+ if (mDisplayBrightnessMapping == null) {
+ return null;
+ }
+ return mDisplayBrightnessMapping.getNitsArray();
}
/**
- * @return Auto brightness brightening levels
+ * @param mode The auto-brightness mode
+ * @param preset The brightness preset. Presets are used on devices that allow users to choose
+ * from a set of predefined options in display auto-brightness settings.
+ * @return The default auto-brightness brightening levels for the specified mode and preset
*/
- public float[] getAutoBrightnessBrighteningLevels() {
- return mBrightnessLevels;
+ public float[] getAutoBrightnessBrighteningLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) {
+ if (mDisplayBrightnessMapping == null) {
+ return null;
+ }
+ return mDisplayBrightnessMapping.getBrightnessArray(mode, preset);
}
/**
@@ -1875,9 +1880,7 @@
+ mAutoBrightnessBrighteningLightDebounceIdle
+ ", mAutoBrightnessDarkeningLightDebounceIdle= "
+ mAutoBrightnessDarkeningLightDebounceIdle
- + ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
- + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
- + ", mBrightnessLevels= " + Arrays.toString(mBrightnessLevels)
+ + ", mDisplayBrightnessMapping= " + mDisplayBrightnessMapping
+ ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
+ ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
+ "\n"
@@ -2568,7 +2571,8 @@
// Idle must be called after interactive, since we fall back to it if needed.
loadAutoBrightnessBrighteningLightDebounceIdle(autoBrightness);
loadAutoBrightnessDarkeningLightDebounceIdle(autoBrightness);
- loadAutoBrightnessDisplayBrightnessMapping(autoBrightness);
+ mDisplayBrightnessMapping = new DisplayBrightnessMappingConfig(mContext, mFlags,
+ autoBrightness, mBacklightToBrightnessSpline);
loadEnableAutoBrightness(autoBrightness);
}
@@ -2633,38 +2637,6 @@
}
}
- /**
- * Loads the auto-brightness display brightness mappings. Internally, this takes care of
- * loading the value from the display config, and if not present, falls back to config.xml.
- */
- private void loadAutoBrightnessDisplayBrightnessMapping(AutoBrightness autoBrightnessConfig) {
- if (mFlags.areAutoBrightnessModesEnabled() && autoBrightnessConfig != null
- && autoBrightnessConfig.getLuxToBrightnessMapping() != null) {
- LuxToBrightnessMapping mapping = autoBrightnessConfig.getLuxToBrightnessMapping();
- final int size = mapping.getMap().getPoint().size();
- mBrightnessLevels = new float[size];
- mBrightnessLevelsLux = new float[size];
- for (int i = 0; i < size; i++) {
- float backlight = mapping.getMap().getPoint().get(i).getSecond().floatValue();
- mBrightnessLevels[i] = mBacklightToBrightnessSpline.interpolate(backlight);
- mBrightnessLevelsLux[i] = mapping.getMap().getPoint().get(i).getFirst()
- .floatValue();
- }
- if (size > 0 && mBrightnessLevelsLux[0] != 0) {
- throw new IllegalArgumentException(
- "The first lux value in the display brightness mapping must be 0");
- }
- } else {
- mBrightnessLevelsNits = getFloatArray(mContext.getResources()
- .obtainTypedArray(com.android.internal.R.array
- .config_autoBrightnessDisplayValuesNits), PowerManager
- .BRIGHTNESS_OFF_FLOAT);
- mBrightnessLevelsLux = getLuxLevels(mContext.getResources()
- .getIntArray(com.android.internal.R.array
- .config_autoBrightnessLevels));
- }
- }
-
private void loadAutoBrightnessAvailableFromConfigXml() {
mAutoBrightnessAvailable = mContext.getResources().getBoolean(
R.bool.config_automatic_brightness_available);
@@ -2977,7 +2949,8 @@
}
private void loadAutoBrightnessConfigsFromConfigXml() {
- loadAutoBrightnessDisplayBrightnessMapping(null /*AutoBrightnessConfig*/);
+ mDisplayBrightnessMapping = new DisplayBrightnessMappingConfig(mContext, mFlags,
+ /* autoBrightnessConfig= */ null, mBacklightToBrightnessSpline);
}
private void loadBrightnessChangeThresholdsFromXml() {
@@ -3347,7 +3320,12 @@
return vals;
}
- private static float[] getLuxLevels(int[] lux) {
+ /**
+ * @param lux The lux array
+ * @return The lux array with 0 appended at the beginning - the first lux value should always
+ * be 0
+ */
+ public static float[] getLuxLevels(int[] lux) {
// The first control point is implicit and always at 0 lux.
float[] levels = new float[lux.length + 1];
for (int i = 0; i < lux.length; i++) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e38d08f..bc3f9dd 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -4573,8 +4573,10 @@
if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
final DisplayPowerControllerInterface displayPowerController =
mDisplayPowerControllers.get(id);
- ready &= displayPowerController.requestPowerState(request,
- waitForNegativeProximity);
+ if (displayPowerController != null) {
+ ready &= displayPowerController.requestPowerState(request,
+ waitForNegativeProximity);
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index 1bd556b..a43f93a 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_MAX;
+
import android.annotation.Nullable;
import android.hardware.display.DisplayManagerInternal;
import android.os.PowerManager;
@@ -56,6 +58,31 @@
}
}
+ @Override
+ public boolean blockScreenOn(Runnable unblocker) {
+ if (mDisplayOffloader == null) {
+ return false;
+ }
+ mDisplayOffloader.onBlockingScreenOn(unblocker);
+ return true;
+ }
+
+ @Override
+ public float[] getAutoBrightnessLevels(int mode) {
+ if (mode < 0 || mode > AUTO_BRIGHTNESS_MODE_MAX) {
+ throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
+ }
+ return mDisplayPowerController.getAutoBrightnessLevels(mode);
+ }
+
+ @Override
+ public float[] getAutoBrightnessLuxLevels(int mode) {
+ if (mode < 0 || mode > AUTO_BRIGHTNESS_MODE_MAX) {
+ throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
+ }
+ return mDisplayPowerController.getAutoBrightnessLuxLevels(mode);
+ }
+
/**
* Start the offload session. The method returns if the session is already active.
* @return Whether the session was started successfully
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2685efe..734381b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -735,7 +735,7 @@
mCdsi = null;
}
- setUpAutoBrightness(resources, handler);
+ setUpAutoBrightness(context, handler);
mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic()
&& !resources.getBoolean(
@@ -1075,7 +1075,7 @@
loadBrightnessRampRates();
loadProximitySensor();
loadNitsRange(mContext.getResources());
- setUpAutoBrightness(mContext.getResources(), mHandler);
+ setUpAutoBrightness(mContext, mHandler);
reloadReduceBrightColours();
setAnimatorRampSpeeds(/* isIdleMode= */ false);
mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
@@ -1145,7 +1145,7 @@
handleBrightnessModeChange();
}
- private void setUpAutoBrightness(Resources resources, Handler handler) {
+ private void setUpAutoBrightness(Context context, Handler handler) {
mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable();
if (!mUseSoftwareAutoBrightnessConfig) {
@@ -1155,21 +1155,19 @@
SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>();
BrightnessMappingStrategy defaultModeBrightnessMapper =
- mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig,
+ mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig,
mDisplayWhiteBalanceController);
brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT,
defaultModeBrightnessMapper);
- final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
+ final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean(
R.bool.config_enableIdleScreenBrightnessMode);
if (isIdleScreenBrightnessEnabled) {
BrightnessMappingStrategy idleModeBrightnessMapper =
- BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig,
- AUTO_BRIGHTNESS_MODE_IDLE,
- mDisplayWhiteBalanceController);
+ BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
+ AUTO_BRIGHTNESS_MODE_IDLE, mDisplayWhiteBalanceController);
if (idleModeBrightnessMapper != null) {
- brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE,
- idleModeBrightnessMapper);
+ brightnessMappers.append(AUTO_BRIGHTNESS_MODE_IDLE, idleModeBrightnessMapper);
}
}
@@ -1181,7 +1179,7 @@
}
if (defaultModeBrightnessMapper != null) {
- final float dozeScaleFactor = resources.getFraction(
+ final float dozeScaleFactor = context.getResources().getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
@@ -1265,14 +1263,14 @@
.getAutoBrightnessBrighteningLightDebounceIdle();
long darkeningLightDebounceIdle = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounceIdle();
- boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+ boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
- int lightSensorWarmUpTimeConfig = resources.getInteger(
+ int lightSensorWarmUpTimeConfig = context.getResources().getInteger(
com.android.internal.R.integer.config_lightSensorWarmupTime);
- int lightSensorRate = resources.getInteger(
+ int lightSensorRate = context.getResources().getInteger(
com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
- int initialLightSensorRate = resources.getInteger(
+ int initialLightSensorRate = context.getResources().getInteger(
com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
if (initialLightSensorRate == -1) {
initialLightSensorRate = lightSensorRate;
@@ -2216,6 +2214,20 @@
}
@Override
+ public float[] getAutoBrightnessLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ // The old DPC is no longer supported
+ return null;
+ }
+
+ @Override
+ public float[] getAutoBrightnessLuxLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ // The old DPC is no longer supported
+ return null;
+ }
+
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
@@ -3645,12 +3657,11 @@
userNits);
}
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
- return BrightnessMappingStrategy.create(resources,
- displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT,
- displayWhiteBalanceController);
+ return BrightnessMappingStrategy.create(context, displayDeviceConfig,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController);
}
HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 52c53f3..7df6114 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -17,7 +17,9 @@
package com.android.server.display;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
+import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -126,6 +128,8 @@
// To enable these logs, run:
// 'adb shell setprop persist.log.tag.DisplayPowerController2 DEBUG && adb reboot'
private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+ private static final String SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME =
+ "Screen on blocked by displayoffload";
// If true, uses the color fade on animation.
// We might want to turn this off if we cannot get a guarantee that the screen
@@ -155,6 +159,7 @@
private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15;
private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
+ private static final int MSG_OFFLOADING_SCREEN_ON_UNBLOCKED = 18;
@@ -339,6 +344,7 @@
// we are waiting for a callback to release it and unblock the screen.
private ScreenOnUnblocker mPendingScreenOnUnblocker;
private ScreenOffUnblocker mPendingScreenOffUnblocker;
+ private Runnable mPendingScreenOnUnblockerByDisplayOffload;
// True if we were in the process of turning off the screen.
// This allows us to recover more gracefully from situations where we abort
@@ -348,10 +354,15 @@
// The elapsed real time when the screen on was blocked.
private long mScreenOnBlockStartRealTime;
private long mScreenOffBlockStartRealTime;
+ private long mScreenOnBlockByDisplayOffloadStartRealTime;
// Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields.
private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED;
+ // Used to deduplicate the displayoffload blocking screen on logic. One block per turning on.
+ // This value is reset when screen on is reported or the blocking is cancelled.
+ private boolean mScreenTurningOnWasBlockedByDisplayOffload;
+
// If the last recorded screen state was dozing or not.
private boolean mDozing;
@@ -472,7 +483,7 @@
private boolean mBootCompleted;
private final DisplayManagerFlags mFlags;
- private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+ private DisplayOffloadSession mDisplayOffloadSession;
/**
* Creates the display power controller.
@@ -614,7 +625,7 @@
mCdsi = null;
}
- setUpAutoBrightness(resources, handler);
+ setUpAutoBrightness(context, handler);
mColorFadeEnabled = mInjector.isColorFadeEnabled()
&& !resources.getBoolean(
@@ -772,6 +783,10 @@
@Override
public void setDisplayOffloadSession(DisplayOffloadSession session) {
+ if (session == mDisplayOffloadSession) {
+ return;
+ }
+ unblockScreenOnByDisplayOffload();
mDisplayOffloadSession = session;
}
@@ -891,7 +906,7 @@
// updated here.
loadBrightnessRampRates();
loadNitsRange(mContext.getResources());
- setUpAutoBrightness(mContext.getResources(), mHandler);
+ setUpAutoBrightness(mContext, mHandler);
reloadReduceBrightColours();
setAnimatorRampSpeeds(/* isIdleMode= */ false);
@@ -962,10 +977,15 @@
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE),
false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+ if (mFlags.areAutoBrightnessModesEnabled()) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_ALS),
+ /* notifyForDescendants= */ false, mSettingsObserver, UserHandle.USER_CURRENT);
+ }
handleBrightnessModeChange();
}
- private void setUpAutoBrightness(Resources resources, Handler handler) {
+ private void setUpAutoBrightness(Context context, Handler handler) {
mUseSoftwareAutoBrightnessConfig = mDisplayDeviceConfig.isAutoBrightnessAvailable();
if (!mUseSoftwareAutoBrightnessConfig) {
@@ -975,16 +995,16 @@
SparseArray<BrightnessMappingStrategy> brightnessMappers = new SparseArray<>();
BrightnessMappingStrategy defaultModeBrightnessMapper =
- mInjector.getDefaultModeBrightnessMapper(resources, mDisplayDeviceConfig,
+ mInjector.getDefaultModeBrightnessMapper(context, mDisplayDeviceConfig,
mDisplayWhiteBalanceController);
brightnessMappers.append(AUTO_BRIGHTNESS_MODE_DEFAULT,
defaultModeBrightnessMapper);
- final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
+ final boolean isIdleScreenBrightnessEnabled = context.getResources().getBoolean(
R.bool.config_enableIdleScreenBrightnessMode);
if (isIdleScreenBrightnessEnabled) {
BrightnessMappingStrategy idleModeBrightnessMapper =
- BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig,
+ BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
AUTO_BRIGHTNESS_MODE_IDLE,
mDisplayWhiteBalanceController);
if (idleModeBrightnessMapper != null) {
@@ -993,6 +1013,13 @@
}
}
+ BrightnessMappingStrategy dozeModeBrightnessMapper =
+ BrightnessMappingStrategy.create(context, mDisplayDeviceConfig,
+ AUTO_BRIGHTNESS_MODE_DOZE, mDisplayWhiteBalanceController);
+ if (mFlags.areAutoBrightnessModesEnabled() && dozeModeBrightnessMapper != null) {
+ brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper);
+ }
+
float userLux = BrightnessMappingStrategy.INVALID_LUX;
float userNits = BrightnessMappingStrategy.INVALID_NITS;
if (mAutomaticBrightnessController != null) {
@@ -1001,7 +1028,7 @@
}
if (defaultModeBrightnessMapper != null) {
- final float dozeScaleFactor = resources.getFraction(
+ final float dozeScaleFactor = context.getResources().getFraction(
R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
@@ -1085,14 +1112,14 @@
.getAutoBrightnessBrighteningLightDebounceIdle();
long darkeningLightDebounceIdle = mDisplayDeviceConfig
.getAutoBrightnessDarkeningLightDebounceIdle();
- boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+ boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean(
R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
- int lightSensorWarmUpTimeConfig = resources.getInteger(
+ int lightSensorWarmUpTimeConfig = context.getResources().getInteger(
R.integer.config_lightSensorWarmupTime);
- int lightSensorRate = resources.getInteger(
+ int lightSensorRate = context.getResources().getInteger(
R.integer.config_autoBrightnessLightSensorRate);
- int initialLightSensorRate = resources.getInteger(
+ int initialLightSensorRate = context.getResources().getInteger(
R.integer.config_autoBrightnessInitialLightSensorRate);
if (initialLightSensorRate == -1) {
initialLightSensorRate = lightSensorRate;
@@ -1336,6 +1363,13 @@
animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition());
state = mPowerState.getScreenState();
+ // Switch to doze auto-brightness mode if needed
+ if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
+ && !mAutomaticBrightnessController.isInIdleMode()) {
+ setAutomaticScreenBrightnessMode(Display.isDozeState(state)
+ ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+ }
+
final boolean userSetBrightnessChanged = mDisplayBrightnessController
.updateUserSetScreenBrightness();
@@ -1735,6 +1769,7 @@
// reporting the display is ready because we only need to ensure the screen is in the
// right power state even as it continues to converge on the desired brightness.
final boolean ready = mPendingScreenOnUnblocker == null
+ && mPendingScreenOnUnblockerByDisplayOffload == null
&& (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
&& !mColorFadeOffAnimator.isStarted()))
&& mPowerState.waitUntilClean(mCleanListener);
@@ -1851,6 +1886,24 @@
}
@Override
+ public float[] getAutoBrightnessLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
+ return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
+ }
+
+ @Override
+ public float[] getAutoBrightnessLuxLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
+ return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
+ }
+
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
@@ -1983,15 +2036,69 @@
}
}
+ private void blockScreenOnByDisplayOffload(DisplayOffloadSession displayOffloadSession) {
+ if (mPendingScreenOnUnblockerByDisplayOffload != null || displayOffloadSession == null) {
+ return;
+ }
+ mScreenTurningOnWasBlockedByDisplayOffload = true;
+
+ Trace.asyncTraceBegin(
+ Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+ mScreenOnBlockByDisplayOffloadStartRealTime = SystemClock.elapsedRealtime();
+
+ mPendingScreenOnUnblockerByDisplayOffload =
+ () -> onDisplayOffloadUnblockScreenOn(displayOffloadSession);
+ if (!displayOffloadSession.blockScreenOn(mPendingScreenOnUnblockerByDisplayOffload)) {
+ mPendingScreenOnUnblockerByDisplayOffload = null;
+ long delay =
+ SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
+ Slog.w(mTag, "Tried blocking screen on for offloading but failed. So, end trace after "
+ + delay + " ms.");
+ Trace.asyncTraceEnd(
+ Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+ return;
+ }
+ Slog.i(mTag, "Blocking screen on for offloading.");
+ }
+
+ private void onDisplayOffloadUnblockScreenOn(DisplayOffloadSession displayOffloadSession) {
+ Message msg = mHandler.obtainMessage(MSG_OFFLOADING_SCREEN_ON_UNBLOCKED,
+ displayOffloadSession);
+ mHandler.sendMessage(msg);
+ }
+
+ private void unblockScreenOnByDisplayOffload() {
+ if (mPendingScreenOnUnblockerByDisplayOffload == null) {
+ return;
+ }
+ mPendingScreenOnUnblockerByDisplayOffload = null;
+ long delay = SystemClock.elapsedRealtime() - mScreenOnBlockByDisplayOffloadStartRealTime;
+ Slog.i(mTag, "Unblocked screen on for offloading after " + delay + " ms");
+ Trace.asyncTraceEnd(
+ Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
+ }
+
private boolean setScreenState(int state) {
return setScreenState(state, false /*reportOnly*/);
}
private boolean setScreenState(int state, boolean reportOnly) {
final boolean isOff = (state == Display.STATE_OFF);
+ final boolean isOn = (state == Display.STATE_ON);
+ final boolean changed = mPowerState.getScreenState() != state;
- if (mPowerState.getScreenState() != state
- || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
+ // If the screen is turning on, give displayoffload a chance to do something before the
+ // screen actually turns on.
+ // TODO(b/316941732): add tests for this displayoffload screen-on blocker.
+ if (isOn && changed && !mScreenTurningOnWasBlockedByDisplayOffload) {
+ blockScreenOnByDisplayOffload(mDisplayOffloadSession);
+ } else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) {
+ // No longer turning screen on, so unblock previous screen on blocking immediately.
+ unblockScreenOnByDisplayOffload();
+ mScreenTurningOnWasBlockedByDisplayOffload = false;
+ }
+
+ if (changed || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
// If we are trying to turn screen off, give policy a chance to do something before we
// actually turn the screen off.
if (isOff && !mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
@@ -2007,8 +2114,9 @@
}
}
- if (!reportOnly && mPowerState.getScreenState() != state
- && readyToUpdateDisplayState()) {
+ if (!reportOnly && changed && readyToUpdateDisplayState()
+ && mPendingScreenOffUnblocker == null
+ && mPendingScreenOnUnblockerByDisplayOffload == null) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
String propertyKey = "debug.tracing.screen_state";
@@ -2060,12 +2168,16 @@
}
// Return true if the screen isn't blocked.
- return mPendingScreenOnUnblocker == null;
+ return mPendingScreenOnUnblocker == null
+ && mPendingScreenOnUnblockerByDisplayOffload == null;
}
private void setReportedScreenState(int state) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state);
mReportedScreenStateToPolicy = state;
+ if (state == REPORTED_TO_POLICY_SCREEN_ON) {
+ mScreenTurningOnWasBlockedByDisplayOffload = false;
+ }
}
private void loadAmbientLightSensor() {
@@ -2813,6 +2925,12 @@
updatePowerState();
}
break;
+ case MSG_OFFLOADING_SCREEN_ON_UNBLOCKED:
+ if (mDisplayOffloadSession == msg.obj) {
+ unblockScreenOnByDisplayOffload();
+ updatePowerState();
+ }
+ break;
case MSG_CONFIGURE_BRIGHTNESS:
BrightnessConfiguration brightnessConfiguration =
(BrightnessConfiguration) msg.obj;
@@ -2905,6 +3023,16 @@
public void onChange(boolean selfChange, Uri uri) {
if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE))) {
handleBrightnessModeChange();
+ } else if (uri.equals(Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS))) {
+ int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL,
+ UserHandle.USER_CURRENT);
+ Slog.i(mTag, "Setting up auto-brightness for preset "
+ + autoBrightnessPresetToString(preset));
+ setUpAutoBrightness(mContext, mHandler);
+ sendUpdatePowerState();
} else {
handleSettingsChange(false /* userSwitch */);
}
@@ -3023,12 +3151,11 @@
userNits);
}
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
- return BrightnessMappingStrategy.create(resources,
- displayDeviceConfig, AUTO_BRIGHTNESS_MODE_DEFAULT,
- displayWhiteBalanceController);
+ return BrightnessMappingStrategy.create(context, displayDeviceConfig,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController);
}
HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index c279184..13acb3f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -237,4 +237,21 @@
* 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/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index f994c05..bcf27b4 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -26,9 +26,12 @@
import android.view.Choreographer;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.server.display.utils.DebugUtils;
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
/**
* Controls the display power state.
@@ -75,10 +78,19 @@
private Runnable mCleanListener;
+ private Executor mAsyncDestroyExecutor;
+
private volatile boolean mStopped;
DisplayPowerState(
DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
+ this(blanker, colorFade, displayId, displayState, BackgroundThread.getExecutor());
+ }
+
+ @VisibleForTesting
+ DisplayPowerState(
+ DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState,
+ Executor asyncDestroyExecutor) {
mHandler = new Handler(true /*async*/);
mChoreographer = Choreographer.getInstance();
mBlanker = blanker;
@@ -86,6 +98,7 @@
mPhotonicModulator = new PhotonicModulator();
mPhotonicModulator.start();
mDisplayId = displayId;
+ mAsyncDestroyExecutor = asyncDestroyExecutor;
// At boot time, we don't know the screen's brightness,
// so prepare to set it to a known state when the state is next applied.
@@ -321,7 +334,7 @@
mStopped = true;
mPhotonicModulator.interrupt();
if (mColorFade != null) {
- mColorFade.destroy();
+ mAsyncDestroyExecutor.execute(mColorFade::destroy);
}
mCleanListener = null;
mHandler.removeCallbacksAndMessages(null);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 22898a6..25576ce 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -25,6 +25,7 @@
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.sidekick.SidekickInternal;
+import android.media.MediaDrm;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -242,6 +243,10 @@
private SurfaceControl.DisplayMode mActiveSfDisplayMode;
// The active display vsync period in SurfaceFlinger
private float mActiveRenderFrameRate;
+ // The current HDCP level supported by the display, 0 indicates unset
+ // values are defined in hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl
+ private int mConnectedHdcpLevel;
+
private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
new DisplayEventReceiver.FrameRateOverride[0];
@@ -675,8 +680,9 @@
mInfo.yDpi = mActiveSfDisplayMode.yDpi;
mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo;
- // Assume that all built-in displays that have secure output (eg. HDCP) also
- // support compositing from gralloc protected buffers.
+ if (mConnectedHdcpLevel != 0) {
+ mStaticDisplayInfo.secure = mConnectedHdcpLevel >= MediaDrm.HDCP_V1;
+ }
if (mStaticDisplayInfo.secure) {
mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
@@ -1093,6 +1099,12 @@
}
}
+ public void onHdcpLevelsChangedLocked(int connectedLevel, int maxLevel) {
+ if (updateHdcpLevelsLocked(connectedLevel, maxLevel)) {
+ updateDeviceInfoLocked();
+ }
+ }
+
public boolean updateActiveModeLocked(int activeSfModeId, float renderFrameRate) {
if (mActiveSfDisplayMode.id == activeSfModeId
&& mActiveRenderFrameRate == renderFrameRate) {
@@ -1118,6 +1130,22 @@
return true;
}
+ public boolean updateHdcpLevelsLocked(int connectedLevel, int maxLevel) {
+ if (connectedLevel > maxLevel) {
+ Slog.w(TAG, "HDCP connected level: " + connectedLevel
+ + " is larger than max level: " + maxLevel
+ + ", ignoring request.");
+ return false;
+ }
+
+ if (mConnectedHdcpLevel == connectedLevel) {
+ return false;
+ }
+
+ mConnectedHdcpLevel = connectedLevel;
+ return true;
+ }
+
public void requestColorModeLocked(int colorMode) {
if (mActiveColorMode == colorMode) {
return;
@@ -1387,6 +1415,7 @@
long renderPeriod);
void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
DisplayEventReceiver.FrameRateOverride[] overrides);
+ void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel);
}
@@ -1420,6 +1449,11 @@
DisplayEventReceiver.FrameRateOverride[] overrides) {
mListener.onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
}
+
+ @Override
+ public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+ mListener.onHdcpLevelsChanged(physicalDisplayId, connectedLevel, maxLevel);
+ }
}
private final class LocalDisplayEventListener implements DisplayEventListener {
@@ -1489,6 +1523,26 @@
device.onFrameRateOverridesChanged(overrides);
}
}
+
+ @Override
+ public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+ if (DEBUG) {
+ Slog.d(TAG, "onHdcpLevelsChanged(physicalDisplayId=" + physicalDisplayId
+ + ", connectedLevel=" + connectedLevel + ", maxLevel=" + maxLevel + ")");
+ }
+ synchronized (getSyncRoot()) {
+ LocalDisplayDevice device = mDevices.get(physicalDisplayId);
+ if (device == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received hdcp levels change for unhandled physical display: "
+ + "physicalDisplayId=" + physicalDisplayId);
+ }
+ return;
+ }
+
+ device.onHdcpLevelsChangedLocked(connectedLevel, maxLevel);
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index 200d88a..01a8d360a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -104,8 +104,7 @@
public void resetHdrConfig(HdrBrightnessData data, int width, int height,
float minimumHdrPercentOfScreen, IBinder displayToken) {
mHdrBrightnessData = data;
- mHdrListener.mHdrMinPixels = minimumHdrPercentOfScreen <= 0 ? -1
- : (float) (width * height) * minimumHdrPercentOfScreen;
+ mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
mHdrListener.unregister(mRegisteredDisplayToken);
@@ -115,7 +114,7 @@
// new token not null and hdr min % of the screen is set, subscribe.
// e.g. for virtual display, HBM data will be missing and HdrListener
// should not be registered
- if (displayToken != null && mHdrListener.mHdrMinPixels > 0) {
+ if (displayToken != null && mHdrListener.mHdrMinPixels >= 0) {
mHdrListener.register(displayToken);
mRegisteredDisplayToken = displayToken;
}
@@ -140,8 +139,11 @@
pw.println(" mDesiredMaxBrightness=" + mDesiredMaxBrightness);
pw.println(" mTransitionRate=" + mTransitionRate);
pw.println(" mDesiredTransitionRate=" + mDesiredTransitionRate);
+ pw.println(" mHdrVisible=" + mHdrVisible);
+ pw.println(" mHdrListener.mHdrMinPixels=" + mHdrListener.mHdrMinPixels);
pw.println(" mHdrBrightnessData=" + (mHdrBrightnessData == null ? "null"
: mHdrBrightnessData.toString()));
+ pw.println(" mHdrListener registered=" + (mRegisteredDisplayToken != null));
pw.println(" mAmbientLux=" + mAmbientLux);
}
diff --git a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
new file mode 100644
index 0000000..6978686
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
@@ -0,0 +1,262 @@
+/*
+ * 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.config;
+
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.util.Spline;
+
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.AutomaticBrightnessController;
+import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.feature.DisplayManagerFlags;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides a mapping between lux and brightness values in order to support auto-brightness.
+ */
+public class DisplayBrightnessMappingConfig {
+
+ private static final String DEFAULT_BRIGHTNESS_MAPPING_KEY =
+ AutoBrightnessModeName._default.getRawName() + "_"
+ + AutoBrightnessSettingName.normal.getRawName();
+
+ /**
+ * Array of desired screen brightness in nits corresponding to the lux values
+ * in the mBrightnessLevelsLuxMap.get(DEFAULT_ID) array. The display brightness is defined as
+ * the measured brightness of an all-white image. The brightness values must be non-negative and
+ * non-decreasing. This must be overridden in platform specific overlays
+ */
+ private float[] mBrightnessLevelsNits;
+
+ /**
+ * Map of arrays of desired screen brightness corresponding to the lux values
+ * in mBrightnessLevelsLuxMap, indexed by the auto-brightness mode and the brightness preset.
+ * The brightness values must be non-negative and non-decreasing. They must be between
+ * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
+ *
+ * The keys are a concatenation of the auto-brightness mode and the brightness preset separated
+ * by an underscore, e.g. default_normal, default_dim, default_bright, doze_normal, doze_dim,
+ * doze_bright.
+ *
+ * The presets are used on devices that allow users to choose from a set of predefined options
+ * in display auto-brightness settings.
+ */
+ private final Map<String, float[]> mBrightnessLevelsMap = new HashMap<>();
+
+ /**
+ * Map of arrays of light sensor lux values to define our levels for auto-brightness support,
+ * indexed by the auto-brightness mode and the brightness preset.
+ *
+ * The first lux value in every array is always 0.
+ *
+ * The control points must be strictly increasing. Each control point corresponds to an entry
+ * in the brightness values arrays. For example, if lux == luxLevels[1] (second element
+ * of the levels array) then the brightness will be determined by brightnessLevels[1] (second
+ * element of the brightness values array).
+ *
+ * Spline interpolation is used to determine the auto-brightness values for lux levels between
+ * these control points.
+ *
+ * The keys are a concatenation of the auto-brightness mode and the brightness preset separated
+ * by an underscore, e.g. default_normal, default_dim, default_bright, doze_normal, doze_dim,
+ * doze_bright.
+ *
+ * The presets are used on devices that allow users to choose from a set of predefined options
+ * in display auto-brightness settings.
+ */
+ private final Map<String, float[]> mBrightnessLevelsLuxMap = new HashMap<>();
+
+ /**
+ * Loads the auto-brightness display brightness mappings. Internally, this takes care of
+ * loading the value from the display config, and if not present, falls back to config.xml.
+ */
+ public DisplayBrightnessMappingConfig(Context context, DisplayManagerFlags flags,
+ AutoBrightness autoBrightnessConfig, Spline backlightToBrightnessSpline) {
+ if (flags.areAutoBrightnessModesEnabled() && autoBrightnessConfig != null
+ && autoBrightnessConfig.getLuxToBrightnessMapping() != null
+ && autoBrightnessConfig.getLuxToBrightnessMapping().size() > 0) {
+ for (LuxToBrightnessMapping mapping
+ : autoBrightnessConfig.getLuxToBrightnessMapping()) {
+ final int size = mapping.getMap().getPoint().size();
+ float[] brightnessLevels = new float[size];
+ float[] brightnessLevelsLux = new float[size];
+ for (int i = 0; i < size; i++) {
+ float backlight = mapping.getMap().getPoint().get(i).getSecond().floatValue();
+ brightnessLevels[i] = backlightToBrightnessSpline.interpolate(backlight);
+ brightnessLevelsLux[i] = mapping.getMap().getPoint().get(i).getFirst()
+ .floatValue();
+ }
+ if (size == 0) {
+ throw new IllegalArgumentException(
+ "A display brightness mapping should not be empty");
+ }
+ if (brightnessLevelsLux[0] != 0) {
+ throw new IllegalArgumentException(
+ "The first lux value in the display brightness mapping must be 0");
+ }
+
+ String key = (mapping.getMode() == null ? "default" : mapping.getMode()) + "_"
+ + (mapping.getSetting() == null ? "normal" : mapping.getSetting());
+ if (mBrightnessLevelsMap.containsKey(key)
+ || mBrightnessLevelsLuxMap.containsKey(key)) {
+ throw new IllegalArgumentException(
+ "A display brightness mapping with key " + key + " already exists");
+ }
+ mBrightnessLevelsMap.put(key, brightnessLevels);
+ mBrightnessLevelsLuxMap.put(key, brightnessLevelsLux);
+ }
+ }
+
+ if (!mBrightnessLevelsMap.containsKey(DEFAULT_BRIGHTNESS_MAPPING_KEY)
+ || !mBrightnessLevelsLuxMap.containsKey(DEFAULT_BRIGHTNESS_MAPPING_KEY)) {
+ mBrightnessLevelsNits = DisplayDeviceConfig.getFloatArray(context.getResources()
+ .obtainTypedArray(com.android.internal.R.array
+ .config_autoBrightnessDisplayValuesNits), PowerManager
+ .BRIGHTNESS_OFF_FLOAT);
+
+ float[] brightnessLevelsLux = DisplayDeviceConfig.getLuxLevels(context.getResources()
+ .getIntArray(com.android.internal.R.array
+ .config_autoBrightnessLevels));
+ mBrightnessLevelsLuxMap.put(DEFAULT_BRIGHTNESS_MAPPING_KEY, brightnessLevelsLux);
+
+ // Load the old configuration in the range [0, 255]. The values need to be normalized
+ // to the range [0, 1].
+ int[] brightnessLevels = context.getResources().getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+ mBrightnessLevelsMap.put(DEFAULT_BRIGHTNESS_MAPPING_KEY,
+ brightnessArrayIntToFloat(brightnessLevels, backlightToBrightnessSpline));
+ }
+ }
+
+ /**
+ * @param mode The auto-brightness mode
+ * @param preset The brightness preset. Presets are used on devices that allow users to choose
+ * from a set of predefined options in display auto-brightness settings.
+ * @return The default auto-brightness brightening ambient lux levels for the specified mode
+ * and preset
+ */
+ public float[] getLuxArray(@AutomaticBrightnessController.AutomaticBrightnessMode int mode,
+ int preset) {
+ return mBrightnessLevelsLuxMap.get(
+ autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset));
+ }
+
+ /**
+ * @return Auto brightness brightening nits levels
+ */
+ public float[] getNitsArray() {
+ return mBrightnessLevelsNits;
+ }
+
+ /**
+ * @param mode The auto-brightness mode
+ * @param preset The brightness preset. Presets are used on devices that allow users to choose
+ * from a set of predefined options in display auto-brightness settings.
+ * @return The default auto-brightness brightening levels for the specified mode and preset
+ */
+ public float[] getBrightnessArray(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) {
+ return mBrightnessLevelsMap.get(
+ autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder brightnessLevelsLuxMapString = new StringBuilder("{");
+ for (Map.Entry<String, float[]> entry : mBrightnessLevelsLuxMap.entrySet()) {
+ brightnessLevelsLuxMapString.append(entry.getKey()).append("=").append(
+ Arrays.toString(entry.getValue())).append(", ");
+ }
+ if (brightnessLevelsLuxMapString.length() > 2) {
+ brightnessLevelsLuxMapString.delete(brightnessLevelsLuxMapString.length() - 2,
+ brightnessLevelsLuxMapString.length());
+ }
+ brightnessLevelsLuxMapString.append("}");
+
+ StringBuilder brightnessLevelsMapString = new StringBuilder("{");
+ for (Map.Entry<String, float[]> entry : mBrightnessLevelsMap.entrySet()) {
+ brightnessLevelsMapString.append(entry.getKey()).append("=").append(
+ Arrays.toString(entry.getValue())).append(", ");
+ }
+ if (brightnessLevelsMapString.length() > 2) {
+ brightnessLevelsMapString.delete(brightnessLevelsMapString.length() - 2,
+ brightnessLevelsMapString.length());
+ }
+ brightnessLevelsMapString.append("}");
+
+ return "mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ + ", mBrightnessLevelsLuxMap= " + brightnessLevelsLuxMapString
+ + ", mBrightnessLevelsMap= " + brightnessLevelsMapString;
+ }
+
+ /**
+ * @param mode The auto-brightness mode
+ * @return The string representing the mode
+ */
+ public static String autoBrightnessModeToString(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
+ switch (mode) {
+ case AUTO_BRIGHTNESS_MODE_DEFAULT -> {
+ return AutoBrightnessModeName._default.getRawName();
+ }
+ case AUTO_BRIGHTNESS_MODE_IDLE -> {
+ return AutoBrightnessModeName.idle.getRawName();
+ }
+ case AUTO_BRIGHTNESS_MODE_DOZE -> {
+ return AutoBrightnessModeName.doze.getRawName();
+ }
+ default -> throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
+ }
+ }
+
+ /**
+ * @param preset The brightness preset. Presets are used on devices that allow users to choose
+ * from a set of predefined options in display auto-brightness settings.
+ * @return The string representing the preset
+ */
+ public static String autoBrightnessPresetToString(int preset) {
+ return switch (preset) {
+ case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM ->
+ AutoBrightnessSettingName.dim.getRawName();
+ case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL ->
+ AutoBrightnessSettingName.normal.getRawName();
+ case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT ->
+ AutoBrightnessSettingName.bright.getRawName();
+ default -> throw new IllegalArgumentException(
+ "Unknown auto-brightness preset value: " + preset);
+ };
+ }
+
+ private float[] brightnessArrayIntToFloat(int[] brightnessInt,
+ Spline backlightToBrightnessSpline) {
+ float[] brightnessFloat = new float[brightnessInt.length];
+ for (int i = 0; i < brightnessInt.length; i++) {
+ brightnessFloat[i] = backlightToBrightnessSpline.interpolate(
+ BrightnessSynchronizer.brightnessIntToFloat(brightnessInt[i]));
+ }
+ return brightnessFloat;
+ }
+}
diff --git a/services/core/java/com/android/server/flags/OWNERS b/services/core/java/com/android/server/flags/OWNERS
index 535a750..60ceb12 100644
--- a/services/core/java/com/android/server/flags/OWNERS
+++ b/services/core/java/com/android/server/flags/OWNERS
@@ -1 +1,2 @@
-per-file pinner.aconfig = edgararriaga@google.com
\ No newline at end of file
+per-file pinner.aconfig = edgararriaga@google.com
+per-file compaction.aconfig = edgararriaga@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/flags/compaction.aconfig b/services/core/java/com/android/server/flags/compaction.aconfig
new file mode 100644
index 0000000..58cc560
--- /dev/null
+++ b/services/core/java/com/android/server/flags/compaction.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.flags"
+
+flag {
+ name: "disable_system_compaction"
+ namespace: "system_performance"
+ description: "This flag controls if all processes compaction should happen during idle maintenance."
+ bug: "314328789"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 64abb81..81204ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -1314,7 +1314,6 @@
*/
protected void disableDevice(
boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
- removeAction(AbsoluteVolumeAudioStatusAction.class);
removeAction(SetAudioVolumeLevelDiscoveryAction.class);
removeAction(ActiveSourceAction.class);
removeAction(ResendCecCommandAction.class);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 29303aa..6157402 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -308,7 +308,6 @@
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
removeAction(OneTouchPlayAction.class);
removeAction(DevicePowerStatusAction.class);
- removeAction(AbsoluteVolumeAudioStatusAction.class);
super.disableDevice(initiatedByCec, callback);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 5831b29..1cd267d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1336,7 +1336,6 @@
removeAction(OneTouchRecordAction.class);
removeAction(TimerRecordingAction.class);
removeAction(NewDeviceAction.class);
- removeAction(AbsoluteVolumeAudioStatusAction.class);
// Remove pending actions.
removeAction(RequestActiveSourceAction.class);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9253706..eaf754d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -3793,6 +3793,11 @@
}
}
});
+
+ // Make sure we switch away from absolute volume behavior (AVB) when entering standby.
+ // We do this because AVB should not be used unless AbsoluteVolumeAudioStatusAction exists,
+ // and the action cannot exist in standby because there are no local devices.
+ checkAndUpdateAbsoluteVolumeBehavior();
}
boolean canGoToStandby() {
@@ -4446,10 +4451,11 @@
* This allows the volume level of the System Audio device to be tracked and set by Android.
*
* Absolute volume behavior requires the following conditions:
- * 1. If the System Audio Device is an Audio System: System Audio Mode is active
- * 2. All AVB-capable audio output devices are already using full/absolute volume behavior
- * 3. CEC volume is enabled
- * 4. The System Audio device supports the <Set Audio Volume Level> message
+ * 1. The device is not in standby or transient to standby
+ * 2. If the System Audio Device is an Audio System: System Audio Mode is active
+ * 3. All AVB-capable audio output devices are already using full/absolute volume behavior
+ * 4. CEC volume is enabled
+ * 5. The System Audio device supports the <Set Audio Volume Level> message
*
* This method enables adjust-only absolute volume behavior on TV panels when conditions
* 1, 2, and 3 are met, but condition 4 is not. This allows TVs to track the volume level of
@@ -4465,10 +4471,16 @@
return;
}
+ // Condition 1: The device is not in standby or transient to standby
+ if (mPowerStatusController != null && isPowerStandbyOrTransient()) {
+ switchToFullVolumeBehavior();
+ return;
+ }
+
HdmiCecLocalDevice localCecDevice;
if (isTvDevice() && tv() != null) {
localCecDevice = tv();
- // Condition 1: TVs need System Audio Mode to be active
+ // Condition 2: TVs need System Audio Mode to be active
// (Doesn't apply to Playback Devices, where if SAM isn't active, we assume the
// TV is the System Audio Device instead.)
if (!isSystemAudioActivated()) {
@@ -4485,7 +4497,7 @@
HdmiDeviceInfo systemAudioDeviceInfo = getDeviceInfo(
localCecDevice.findAudioReceiverAddress());
- // Condition 2: All AVB-capable audio outputs already use full/absolute volume behavior
+ // Condition 3: All AVB-capable audio outputs already use full/absolute volume behavior
// We only need to check the first AVB-capable audio output because only TV panels
// have more than one of them, and they always have the same volume behavior.
@AudioManager.DeviceVolumeBehavior int currentVolumeBehavior =
@@ -4493,7 +4505,7 @@
boolean alreadyUsingFullOrAbsoluteVolume =
FULL_AND_ABSOLUTE_VOLUME_BEHAVIORS.contains(currentVolumeBehavior);
- // Condition 3: CEC volume is enabled
+ // Condition 4: CEC volume is enabled
boolean cecVolumeEnabled =
getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED;
@@ -4509,7 +4521,7 @@
return;
}
- // Condition 4: The System Audio device supports <Set Audio Volume Level>
+ // Condition 5: The System Audio device supports <Set Audio Volume Level>
switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
case DeviceFeatures.FEATURE_SUPPORTED:
if (currentVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
@@ -4556,6 +4568,8 @@
* are currently used. Removes the action for handling volume updates for these behaviors.
*/
private void switchToFullVolumeBehavior() {
+ Slog.d(TAG, "Switching to full volume behavior");
+
if (playback() != null) {
playback().removeAvbAudioStatusAction();
} else if (tv() != null) {
@@ -4597,12 +4611,14 @@
// Otherwise, enable adjust-only AVB on TVs only.
if (systemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport()
== DeviceFeatures.FEATURE_SUPPORTED) {
+ Slog.d(TAG, "Enabling absolute volume behavior");
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior(
device, volumeInfo, mServiceThreadExecutor,
mAbsoluteVolumeChangedListener, true);
}
} else if (tv() != null) {
+ Slog.d(TAG, "Enabling adjust-only absolute volume behavior");
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeAdjustOnlyBehavior(
device, volumeInfo, mServiceThreadExecutor,
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 24e23003..087c525 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2523,9 +2523,9 @@
// Native callback.
@SuppressWarnings("unused")
private int interceptMotionBeforeQueueingNonInteractive(int displayId,
- long whenNanos, int policyFlags) {
+ int source, int action, long whenNanos, int policyFlags) {
return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive(
- displayId, whenNanos, policyFlags);
+ displayId, source, action, whenNanos, policyFlags);
}
// Native callback.
@@ -2901,8 +2901,8 @@
* processing when the device is in a non-interactive state since these events are normally
* dropped.
*/
- int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
- int policyFlags);
+ int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+ long whenNanos, int policyFlags);
/**
* This callback is invoked just before the key is about to be sent to an application.
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index c9668a2..be8d2a4 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -86,9 +86,9 @@
(reason) -> updateKeyRepeatInfo()),
Map.entry(Settings.System.getUriFor(Settings.System.SHOW_ROTARY_INPUT),
(reason) -> updateShowRotaryInput()),
- Map.entry(Settings.System.getUriFor(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS),
+ Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS),
(reason) -> updateAccessibilityBounceKeys()),
- Map.entry(Settings.System.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS),
+ Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS),
(reason) -> updateAccessibilityStickyKeys()));
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0d29b7d..c4d94ee 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1619,7 +1619,7 @@
if (userId != currentUserId) {
return;
}
- mSettings.switchCurrentUser(currentUserId, !mSystemReady);
+ mSettings.switchCurrentUser(currentUserId);
if (mSystemReady) {
// We need to rebuild IMEs.
buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
@@ -1693,7 +1693,7 @@
mLastSwitchUserId = userId;
// mSettings should be created before buildInputMethodListLocked
- mSettings = new InputMethodSettings(mMethodMap, userId, !mSystemReady);
+ mSettings = new InputMethodSettings(mMethodMap, userId);
AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
mSwitchingController =
@@ -1811,7 +1811,7 @@
// copy-on-write settings.
final boolean useCopyOnWriteSettings =
!mSystemReady || !mUserManagerInternal.isUserUnlockingOrUnlocked(newUserId);
- mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
+ mSettings.switchCurrentUser(newUserId);
// Additional subtypes should be reset when the user is changed
AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -1873,8 +1873,7 @@
if (!mSystemReady) {
mSystemReady = true;
final int currentUserId = mSettings.getCurrentUserId();
- mSettings.switchCurrentUser(currentUserId,
- !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId));
+ mSettings.switchCurrentUser(currentUserId);
mStatusBarManagerInternal =
LocalServices.getService(StatusBarManagerInternal.class);
hideStatusBarIconLocked();
@@ -2023,7 +2022,7 @@
//TODO(b/197848765): This can be optimized by caching multi-user methodMaps/methodList.
//TODO(b/210039666): use cache.
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
final InputMethodInfo imi = methodMap.get(settings.getSelectedInputMethod());
return imi != null && imi.supportsStylusHandwriting();
}
@@ -2059,7 +2058,7 @@
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
methodList, directBootAwareness);
- settings = new InputMethodSettings(methodMap, userId, true /* copyOnWrite */);
+ settings = new InputMethodSettings(methodMap, userId);
}
// filter caller's access to input methods
methodList.removeIf(imi ->
@@ -2077,7 +2076,7 @@
settings = mSettings;
} else {
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- settings = new InputMethodSettings(methodMap, userId, true /* copyOnWrite */);
+ settings = new InputMethodSettings(methodMap, userId);
methodList = settings.getEnabledInputMethodListLocked();
}
// filter caller's access to input methods
@@ -2157,7 +2156,7 @@
if (imi == null) {
return Collections.emptyList();
}
- final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
if (!canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings)) {
return Collections.emptyList();
}
@@ -4134,7 +4133,7 @@
}
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
return settings.getLastInputMethodSubtypeLocked();
}
}
@@ -4186,7 +4185,7 @@
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
methodList, DirectBootAwareness.AUTO);
- final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap,
mPackageManagerInternal, callingUid);
}
@@ -4215,8 +4214,7 @@
final boolean currentUser = (mSettings.getCurrentUserId() == userId);
final InputMethodSettings settings = currentUser
? mSettings
- : new InputMethodSettings(queryMethodMapForUser(userId), userId,
- !mUserManagerInternal.isUserUnlocked(userId));
+ : new InputMethodSettings(queryMethodMapForUser(userId), userId);
if (!settings.setEnabledInputMethodSubtypes(imeId, subtypeHashCodes)) {
return;
}
@@ -5264,21 +5262,21 @@
*/
@GuardedBy("ImfLock.class")
private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
- List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
- .getEnabledInputMethodsAndSubtypeListLocked();
-
if (enabled) {
- for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
- if (pair.first.equals(id)) {
- // We are enabling this input method, but it is already enabled.
- // Nothing to do. The previous state was enabled.
- return true;
- }
+ final String enabledImeIdsStr = mSettings.getEnabledInputMethodsStr();
+ final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
+ enabledImeIdsStr, id);
+ if (TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) {
+ // We are enabling this input method, but it is already enabled.
+ // Nothing to do. The previous state was enabled.
+ return true;
}
- mSettings.appendAndPutEnabledInputMethodLocked(id);
+ mSettings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
// Previous state was disabled.
return false;
} else {
+ final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
+ .getEnabledInputMethodsAndSubtypeListLocked();
StringBuilder builder = new StringBuilder();
if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
builder, enabledInputMethodsList, id)) {
@@ -5365,7 +5363,7 @@
}
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
return settings.getCurrentInputMethodSubtypeForNonCurrentUsers();
}
}
@@ -5438,7 +5436,7 @@
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
methodList, DirectBootAwareness.AUTO);
- InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true);
+ InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
return methodMap.get(settings.getSelectedInputMethod());
}
@@ -5465,7 +5463,7 @@
return true;
}
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
if (!methodMap.containsKey(imeId)
|| !settings.getEnabledInputMethodListLocked().contains(methodMap.get(imeId))) {
return false; // IME is not found or not enabled.
@@ -5604,15 +5602,16 @@
return true;
}
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(methodMap,
- userId, false);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
if (!methodMap.containsKey(imeId)) {
return false; // IME is not found.
}
if (enabled) {
- if (!settings.getEnabledInputMethodListLocked().contains(
- methodMap.get(imeId))) {
- settings.appendAndPutEnabledInputMethodLocked(imeId);
+ final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
+ final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
+ enabledImeIdsStr, imeId);
+ if (!TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) {
+ settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
}
} else {
settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
@@ -6346,19 +6345,17 @@
}
} else {
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false);
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
if (enabled) {
if (!methodMap.containsKey(imeId)) {
failedToEnableUnknownIme = true;
} else {
- for (InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
- if (TextUtils.equals(imi.getId(), imeId)) {
- previouslyEnabled = true;
- break;
- }
- }
+ final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
+ final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
+ enabledImeIdsStr, imeId);
+ previouslyEnabled = TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr);
if (!previouslyEnabled) {
- settings.appendAndPutEnabledInputMethodLocked(imeId);
+ settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
}
}
} else {
@@ -6491,7 +6488,7 @@
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
methodMap, methodList, DirectBootAwareness.AUTO);
final InputMethodSettings settings = new InputMethodSettings(
- methodMap, userId, false);
+ methodMap, userId);
nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext,
methodList);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 773293f..547fd2f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -213,9 +213,6 @@
public static class InputMethodSettings {
private final ArrayMap<String, InputMethodInfo> mMethodMap;
- private boolean mCopyOnWrite = false;
- @NonNull
- private String mEnabledInputMethodsStrCache = "";
@UserIdInt
private int mCurrentUserId;
@@ -229,54 +226,21 @@
}
}
- private static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
- String enabledInputMethodsStr,
- TextUtils.SimpleStringSplitter inputMethodSplitter,
- TextUtils.SimpleStringSplitter subtypeSplitter) {
- ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<>();
- if (TextUtils.isEmpty(enabledInputMethodsStr)) {
- return imsList;
- }
- inputMethodSplitter.setString(enabledInputMethodsStr);
- while (inputMethodSplitter.hasNext()) {
- String nextImsStr = inputMethodSplitter.next();
- subtypeSplitter.setString(nextImsStr);
- if (subtypeSplitter.hasNext()) {
- ArrayList<String> subtypeHashes = new ArrayList<>();
- // The first element is ime id.
- String imeId = subtypeSplitter.next();
- while (subtypeSplitter.hasNext()) {
- subtypeHashes.add(subtypeSplitter.next());
- }
- imsList.add(new Pair<>(imeId, subtypeHashes));
- }
- }
- return imsList;
- }
-
- InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId,
- boolean copyOnWrite) {
+ InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
mMethodMap = methodMap;
- switchCurrentUser(userId, copyOnWrite);
+ switchCurrentUser(userId);
}
/**
* Must be called when the current user is changed.
*
* @param userId The user ID.
- * @param copyOnWrite If {@code true}, for each settings key
- * (e.g. {@link Settings.Secure#ACTION_INPUT_METHOD_SUBTYPE_SETTINGS}) we use the actual
- * settings on the {@link Settings.Secure} until we do the first write operation.
*/
- void switchCurrentUser(@UserIdInt int userId, boolean copyOnWrite) {
+ void switchCurrentUser(@UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "--- Switch the current user from " + mCurrentUserId + " to " + userId);
}
- if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) {
- mEnabledInputMethodsStrCache = "";
- }
mCurrentUserId = userId;
- mCopyOnWrite = copyOnWrite;
}
private void putString(@NonNull String key, @Nullable String str) {
@@ -351,28 +315,30 @@
}
List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
- return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(),
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR),
- new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR));
- }
-
- List<String> getEnabledInputMethodNames() {
- List<String> result = new ArrayList<>();
- for (Pair<String, ArrayList<String>> pair :
- getEnabledInputMethodsAndSubtypeListLocked()) {
- result.add(pair.first);
+ final String enabledInputMethodsStr = getEnabledInputMethodsStr();
+ final TextUtils.SimpleStringSplitter inputMethodSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+ final TextUtils.SimpleStringSplitter subtypeSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+ final ArrayList<Pair<String, ArrayList<String>>> imsList = new ArrayList<>();
+ if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+ return imsList;
}
- return result;
- }
-
- void appendAndPutEnabledInputMethodLocked(String id) {
- if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
- // Add in the newly enabled input method.
- putEnabledInputMethodsStr(id);
- } else {
- putEnabledInputMethodsStr(
- mEnabledInputMethodsStrCache + INPUT_METHOD_SEPARATOR + id);
+ inputMethodSplitter.setString(enabledInputMethodsStr);
+ while (inputMethodSplitter.hasNext()) {
+ String nextImsStr = inputMethodSplitter.next();
+ subtypeSplitter.setString(nextImsStr);
+ if (subtypeSplitter.hasNext()) {
+ ArrayList<String> subtypeHashes = new ArrayList<>();
+ // The first element is ime id.
+ String imeId = subtypeSplitter.next();
+ while (subtypeSplitter.hasNext()) {
+ subtypeHashes.add(subtypeSplitter.next());
+ }
+ imsList.add(new Pair<>(imeId, subtypeHashes));
+ }
}
+ return imsList;
}
/**
@@ -431,18 +397,11 @@
} else {
putString(Settings.Secure.ENABLED_INPUT_METHODS, str);
}
- // TODO: Update callers of putEnabledInputMethodsStr to make str @NonNull.
- mEnabledInputMethodsStrCache = (str != null ? str : "");
}
@NonNull
String getEnabledInputMethodsStr() {
- mEnabledInputMethodsStrCache = getString(Settings.Secure.ENABLED_INPUT_METHODS, "");
- if (DEBUG) {
- Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache
- + ", " + mCurrentUserId);
- }
- return mEnabledInputMethodsStrCache;
+ return getString(Settings.Secure.ENABLED_INPUT_METHODS, "");
}
private void saveSubtypeHistory(
@@ -875,8 +834,6 @@
public void dumpLocked(final Printer pw, final String prefix) {
pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
- pw.println(prefix + "mCopyOnWrite=" + mCopyOnWrite);
- pw.println(prefix + "mEnabledInputMethodsStrCache=" + mEnabledInputMethodsStrCache);
}
}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index f2dcba4..5514ec7 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -59,6 +59,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -67,7 +68,7 @@
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.RuleMetadata;
import com.android.server.pm.PackageManagerServiceUtils;
-import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.PackageParserUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -141,7 +142,7 @@
return new AppIntegrityManagerServiceImpl(
context,
LocalServices.getService(PackageManagerInternal.class),
- PackageParser2::forParsingFileWithDefaults,
+ PackageParserUtils::forParsingFileWithDefaults,
RuleEvaluationEngine.getRuleEvaluationEngine(),
IntegrityFileManager.getInstance(),
handlerThread.getThreadHandler());
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 27b01a5..9c4225d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -67,6 +67,7 @@
import android.location.LocationManager;
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;
@@ -1380,7 +1381,11 @@
location.setExtras(mLocationExtras.getBundle());
- reportLocation(LocationResult.wrap(location).validate());
+ try {
+ reportLocation(LocationResult.wrap(location).validate());
+ } catch (BadLocationException e) {
+ throw new IllegalArgumentException(e);
+ }
if (mStarted) {
mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1751,7 +1756,11 @@
}
}
- reportLocation(LocationResult.wrap(locations).validate());
+ try {
+ reportLocation(LocationResult.wrap(locations).validate());
+ } catch (BadLocationException e) {
+ throw new IllegalArgumentException(e);
+ }
}
Runnable[] listeners;
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 78c8cde..403b421 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -19,6 +19,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import android.content.Context;
+import android.location.flags.Flags;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -48,6 +49,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* Handles network connection requests and network state change updates for AGPS data download.
@@ -91,6 +93,10 @@
// network with SUPL connectivity or report an error.
private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000;
+ // If the chipset does not request to release a SUPL connection before the specified timeout in
+ // milliseconds, the connection will be automatically released.
+ private static final long SUPL_CONNECTION_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1);
+
private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5;
// Keeps track of networks and their state as notified by the network request callbacks.
@@ -121,6 +127,8 @@
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
private final PowerManager.WakeLock mWakeLock;
+ private final Object mSuplConnectionReleaseOnTimeoutToken = new Object();
+
/**
* Network attributes needed when updating HAL about network connectivity status changes.
*/
@@ -609,6 +617,13 @@
mSuplConnectivityCallback,
mHandler,
SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);
+ if (Flags.releaseSuplConnectionOnTimeout()) {
+ // Schedule to release the SUPL connection after timeout
+ mHandler.removeCallbacksAndMessages(mSuplConnectionReleaseOnTimeoutToken);
+ mHandler.postDelayed(() -> handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN),
+ mSuplConnectionReleaseOnTimeoutToken,
+ SUPL_CONNECTION_TIMEOUT_MILLIS);
+ }
} catch (RuntimeException e) {
Log.e(TAG, "Failed to request network.", e);
mSuplConnectivityCallback = null;
@@ -639,6 +654,10 @@
Log.d(TAG, message);
}
+ if (Flags.releaseSuplConnectionOnTimeout()) {
+ // Remove pending task to avoid releasing an incorrect connection
+ mHandler.removeCallbacksAndMessages(mSuplConnectionReleaseOnTimeoutToken);
+ }
if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
return;
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 91e6a80..7d44aec 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -64,6 +64,7 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.LocationResult.BadLocationException;
import android.location.altitude.AltitudeConverter;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
@@ -910,7 +911,8 @@
< getRequest().getMinUpdateIntervalMillis() - maxJitterMs) {
if (D) {
Log.v(TAG, mName + " provider registration " + getIdentity()
- + " dropped delivery - too fast");
+ + " dropped delivery - too fast (deltaMs="
+ + deltaMs + ").");
}
return false;
}
@@ -2574,29 +2576,17 @@
@GuardedBy("mMultiplexerLock")
@Nullable
private LocationResult processReportedLocation(LocationResult locationResult) {
- LocationResult processed = locationResult.filter(location -> {
- if (!location.isMock()) {
- if (location.getLatitude() == 0 && location.getLongitude() == 0) {
- Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
- return false;
- }
- }
-
- if (!location.isComplete()) {
- Log.e(TAG, "blocking incomplete location from " + mName + " provider");
- return false;
- }
-
- return true;
- });
- if (processed == null) {
+ try {
+ locationResult.validate();
+ } catch (BadLocationException e) {
+ Log.e(TAG, "Dropping invalid locations: " + e);
return null;
}
// Attempt to add a missing MSL altitude on behalf of the provider.
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_LOCATION,
"enable_location_provider_manager_msl", true)) {
- return processed.map(location -> {
+ return locationResult.map(location -> {
if (!location.hasMslAltitude() && location.hasAltitude()) {
try {
Location locationCopy = new Location(location);
@@ -2626,7 +2616,7 @@
return location;
});
}
- return processed;
+ return locationResult;
}
@GuardedBy("mMultiplexerLock")
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index 52b04d4..4efacd7 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
+import android.location.LocationResult.BadLocationException;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -55,7 +56,11 @@
Location location = new Location(l);
location.setIsFromMockProvider(true);
mLocation = location;
- reportLocation(LocationResult.wrap(location).validate());
+ try {
+ reportLocation(LocationResult.wrap(location).validate());
+ } catch (BadLocationException e) {
+ throw new IllegalArgumentException(e);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 05966da..a597edd 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -305,7 +305,7 @@
return;
}
- reportLocation(LocationResult.wrap(location).validate());
+ reportLocation(LocationResult.wrap(location));
}
}
@@ -316,8 +316,7 @@
if (mProxy != this) {
return;
}
-
- reportLocation(LocationResult.wrap(locations).validate());
+ reportLocation(LocationResult.wrap(locations));
}
}
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
similarity index 96%
rename from services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
rename to services/core/java/com/android/server/media/AudioManagerRouteController.java
index 8504495..087f5a6 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -57,12 +57,10 @@
*
* <p>This implementation obtains and manages all routes via {@link AudioManager}, with the
* exception of {@link AudioManager#handleBluetoothActiveDeviceChanged inactive bluetooth} routes
- * which are managed by {@link AudioPoliciesBluetoothRouteController}, which depends on the
- * bluetooth stack (for example {@link BluetoothAdapter}.
+ * which are managed by {@link BluetoothDeviceRoutesManager}, which depends on the
+ * bluetooth stack ({@link BluetoothAdapter} and related classes).
*/
-// TODO: b/305199571 - Rename this class to avoid the AudioPolicies prefix, which has been flagged
-// by the audio team as a confusing name.
-/* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
+/* package */ final class AudioManagerRouteController implements DeviceRouteController {
private static final String TAG = SystemMediaRoute2Provider.TAG;
@NonNull
@@ -77,7 +75,7 @@
@NonNull private final AudioManager mAudioManager;
@NonNull private final Handler mHandler;
@NonNull private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
- @NonNull private final AudioPoliciesBluetoothRouteController mBluetoothRouteController;
+ @NonNull private final BluetoothDeviceRoutesManager mBluetoothRouteController;
@NonNull
private final Map<String, MediaRoute2InfoHolder> mRouteIdToAvailableDeviceRoutes =
@@ -87,6 +85,8 @@
@NonNull private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallbackImpl();
+ @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus;
+
@NonNull
private final AudioManager.OnDevicesForAttributesChangedListener
mOnDevicesForAttributesChangedListener = this::onDevicesForAttributesChangedListener;
@@ -101,7 +101,7 @@
Manifest.permission.MODIFY_AUDIO_ROUTING,
Manifest.permission.QUERY_AUDIO_STATE
})
- /* package */ AudioPoliciesDeviceRouteController(
+ /* package */ AudioManagerRouteController(
@NonNull Context context,
@NonNull AudioManager audioManager,
@NonNull Looper looper,
@@ -113,8 +113,12 @@
mHandler = new Handler(Objects.requireNonNull(looper));
mStrategyForMedia = Objects.requireNonNull(strategyForMedia);
mOnDeviceRouteChangedListener = Objects.requireNonNull(onDeviceRouteChangedListener);
+
+ mBuiltInSpeakerSuitabilityStatus =
+ DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
+
mBluetoothRouteController =
- new AudioPoliciesBluetoothRouteController(
+ new BluetoothDeviceRoutesManager(
mContext, btAdapter, this::rebuildAvailableRoutesAndNotify);
// Just build routes but don't notify. The caller may not expect the listener to be invoked
// before this constructor has finished executing.
@@ -373,14 +377,19 @@
// from getting an id using BluetoothRouteController#getRouteIdForBluetoothAddress.
routeId = systemRouteInfo.mDefaultRouteId;
}
- return new MediaRoute2Info.Builder(routeId, humanReadableName)
+ MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(routeId, humanReadableName)
.setType(systemRouteInfo.mMediaRoute2InfoType)
.setAddress(address)
.setSystemRoute(true)
.addFeature(FEATURE_LIVE_AUDIO)
.addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+
+ if (systemRouteInfo.mMediaRoute2InfoType == MediaRoute2Info.TYPE_BUILTIN_SPEAKER) {
+ builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus);
+ }
+
+ return builder.build();
}
/**
diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
similarity index 95%
rename from services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
rename to services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
index 7cf3983..8119628 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
@@ -58,9 +58,7 @@
* <p>This class also serves as ground truth for assigning {@link MediaRoute2Info#getId() route ids}
* for bluetooth routes via {@link #getRouteIdForBluetoothAddress}.
*/
-// TODO: b/305199571 - Rename this class to remove the RouteController suffix, which causes
-// confusion with the BluetoothRouteController interface.
-/* package */ class AudioPoliciesBluetoothRouteController {
+/* package */ class BluetoothDeviceRoutesManager {
private static final String TAG = SystemMediaRoute2Provider.TAG;
private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
@@ -86,7 +84,7 @@
@NonNull
private final BluetoothProfileMonitor mBluetoothProfileMonitor;
- AudioPoliciesBluetoothRouteController(@NonNull Context context,
+ BluetoothDeviceRoutesManager(@NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
this(context, bluetoothAdapter,
@@ -94,7 +92,7 @@
}
@VisibleForTesting
- AudioPoliciesBluetoothRouteController(@NonNull Context context,
+ BluetoothDeviceRoutesManager(@NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull BluetoothProfileMonitor bluetoothProfileMonitor,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
@@ -276,7 +274,7 @@
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_OFF
|| state == BluetoothAdapter.STATE_TURNING_OFF) {
- synchronized (AudioPoliciesBluetoothRouteController.this) {
+ synchronized (BluetoothDeviceRoutesManager.this) {
mBluetoothRoutes.clear();
}
notifyBluetoothRoutesUpdated();
@@ -284,7 +282,7 @@
updateBluetoothRoutes();
boolean shouldCallListener;
- synchronized (AudioPoliciesBluetoothRouteController.this) {
+ synchronized (BluetoothDeviceRoutesManager.this) {
shouldCallListener = !mBluetoothRoutes.isEmpty();
}
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 9f175a9..dff0adf 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -65,7 +65,7 @@
if (strategyForMedia != null
&& btAdapter != null
&& Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- return new AudioPoliciesDeviceRouteController(
+ return new AudioManagerRouteController(
context,
audioManager,
looper,
@@ -81,6 +81,30 @@
}
}
+ /** Returns device route availability status. */
+ @MediaRoute2Info.SuitabilityStatus
+ static int getBuiltInSpeakerSuitabilityStatus(@NonNull Context context) {
+ if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ // Route is always suitable if the flag is disabled.
+ return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+ }
+
+ int availabilityStatus =
+ context.getResources()
+ .getInteger(
+ com.android.internal.R.integer
+ .config_mediaRouter_builtInSpeakerSuitability);
+
+ switch (availabilityStatus) {
+ case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER:
+ case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER:
+ case MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER:
+ return availabilityStatus;
+ default:
+ return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+ }
+ }
+
/** Returns the currently selected device (built-in or wired) route. */
@NonNull
MediaRoute2Info getSelectedRoute();
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index c0f2834..65b0ad0 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -72,6 +72,8 @@
@NonNull
private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+ @MediaRoute2Info.SuitabilityStatus private final int mBuiltInSpeakerSuitabilityStatus;
+
private int mDeviceVolume;
private MediaRoute2Info mDeviceRoute;
@@ -90,6 +92,9 @@
mAudioManager = audioManager;
mAudioService = audioService;
+ mBuiltInSpeakerSuitabilityStatus =
+ DeviceRouteController.getBuiltInSpeakerSuitabilityStatus(mContext);
+
AudioRoutesInfo newAudioRoutes = null;
try {
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
@@ -165,19 +170,28 @@
}
synchronized (this) {
- return new MediaRoute2Info.Builder(
- DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
- .setVolumeHandling(mAudioManager.isVolumeFixed()
- ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
- : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolume(mDeviceVolume)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setType(type)
- .addFeature(FEATURE_LIVE_AUDIO)
- .addFeature(FEATURE_LIVE_VIDEO)
- .addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
+ MediaRoute2Info.Builder builder =
+ new MediaRoute2Info.Builder(
+ DEVICE_ROUTE_ID,
+ mContext.getResources().getText(name).toString())
+ .setVolumeHandling(
+ mAudioManager.isVolumeFixed()
+ ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+ : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mDeviceVolume)
+ .setVolumeMax(
+ mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setType(type)
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .addFeature(FEATURE_LIVE_VIDEO)
+ .addFeature(FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+
+ if (type == TYPE_BUILTIN_SPEAKER) {
+ builder.setSuitabilityStatus(mBuiltInSpeakerSuitabilityStatus);
+ }
+
+ return builder.build();
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 8149847..1bc2a5e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -24,6 +24,7 @@
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import com.android.internal.annotations.GuardedBy;
@@ -54,8 +55,15 @@
mCallback = callback;
}
- public abstract void requestCreateSession(long requestId, String packageName, String routeId,
- @Nullable Bundle sessionHints);
+ public abstract void requestCreateSession(
+ long requestId,
+ String packageName,
+ String routeId,
+ @Nullable Bundle sessionHints,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName);
+
public abstract void releaseSession(long requestId, String sessionId);
public abstract void updateDiscoveryPreference(
@@ -63,7 +71,14 @@
public abstract void selectRoute(long requestId, String sessionId, String routeId);
public abstract void deselectRoute(long requestId, String sessionId, String routeId);
- public abstract void transferToRoute(long requestId, String sessionId, String routeId);
+
+ public abstract void transferToRoute(
+ long requestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ String sessionId,
+ String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason);
public abstract void setRouteVolume(long requestId, String routeId, int volume);
public abstract void setSessionVolume(long requestId, String sessionId, int volume);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 330818e..ae889d8 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -98,8 +98,14 @@
}
@Override
- public void requestCreateSession(long requestId, String packageName, String routeId,
- Bundle sessionHints) {
+ public void requestCreateSession(
+ long requestId,
+ String packageName,
+ String routeId,
+ Bundle sessionHints,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
if (mConnectionReady) {
mActiveConnection.requestCreateSession(requestId, packageName, routeId, sessionHints);
updateBinding();
@@ -141,7 +147,13 @@
}
@Override
- public void transferToRoute(long requestId, String sessionId, String routeId) {
+ public void transferToRoute(
+ long requestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ String sessionId,
+ String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason) {
if (mConnectionReady) {
mActiveConnection.transferToRoute(requestId, sessionId, routeId);
}
@@ -649,6 +661,14 @@
+ "Disallowed route: "
+ route);
}
+
+ if (route.getSuitabilityStatus()
+ == MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER) {
+ throw new SecurityException(
+ "Only the system is allowed to set not suitable for transfer status. "
+ + "Disallowed route: "
+ + route);
+ }
}
Connection connection = mConnectionRef.get();
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5e18727..06a8d98 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -337,18 +337,47 @@
}
}
- public void requestCreateSessionWithRouter2(@NonNull IMediaRouter2 router, int requestId,
- long managerRequestId, @NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route, Bundle sessionHints) {
+ public void requestCreateSessionWithRouter2(
+ @NonNull IMediaRouter2 router,
+ int requestId,
+ long managerRequestId,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ Bundle sessionHints,
+ @Nullable UserHandle transferInitiatorUserHandle,
+ @Nullable String transferInitiatorPackageName) {
Objects.requireNonNull(router, "router must not be null");
Objects.requireNonNull(oldSession, "oldSession must not be null");
Objects.requireNonNull(route, "route must not be null");
+ synchronized (mLock) {
+ if (managerRequestId == MediaRoute2ProviderService.REQUEST_ID_NONE
+ || transferInitiatorUserHandle == null
+ || transferInitiatorPackageName == null) {
+ final IBinder binder = router.asBinder();
+ final RouterRecord routerRecord = mAllRouterRecords.get(binder);
+
+ transferInitiatorUserHandle = Binder.getCallingUserHandle();
+ if (routerRecord != null) {
+ transferInitiatorPackageName = routerRecord.mPackageName;
+ } else {
+ transferInitiatorPackageName = mContext.getPackageName();
+ }
+ }
+ }
+
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestCreateSessionWithRouter2Locked(requestId, managerRequestId,
- router, oldSession, route, sessionHints);
+ requestCreateSessionWithRouter2Locked(
+ requestId,
+ managerRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ router,
+ oldSession,
+ route,
+ sessionHints);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -399,10 +428,11 @@
throw new IllegalArgumentException("uniqueSessionId must not be empty");
}
+ UserHandle userHandle = Binder.getCallingUserHandle();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- transferToRouteWithRouter2Locked(router, uniqueSessionId, route);
+ transferToRouteWithRouter2Locked(router, userHandle, uniqueSessionId, route);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -588,16 +618,28 @@
}
}
- public void requestCreateSessionWithManager(@NonNull IMediaRouter2Manager manager,
- int requestId, @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+ public void requestCreateSessionWithManager(
+ @NonNull IMediaRouter2Manager manager,
+ int requestId,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(manager, "manager must not be null");
Objects.requireNonNull(oldSession, "oldSession must not be null");
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route);
+ requestCreateSessionWithManagerLocked(
+ requestId,
+ manager,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -640,18 +682,32 @@
}
}
- public void transferToRouteWithManager(@NonNull IMediaRouter2Manager manager, int requestId,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ public void transferToRouteWithManager(
+ @NonNull IMediaRouter2Manager manager,
+ int requestId,
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
Objects.requireNonNull(manager, "manager must not be null");
if (TextUtils.isEmpty(uniqueSessionId)) {
throw new IllegalArgumentException("uniqueSessionId must not be empty");
}
Objects.requireNonNull(route, "route must not be null");
+ Objects.requireNonNull(transferInitiatorUserHandle);
+ Objects.requireNonNull(transferInitiatorPackageName);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- transferToRouteWithManagerLocked(requestId, manager, uniqueSessionId, route);
+ transferToRouteWithManagerLocked(
+ requestId,
+ manager,
+ uniqueSessionId,
+ route,
+ RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1038,9 +1094,15 @@
}
@GuardedBy("mLock")
- private void requestCreateSessionWithRouter2Locked(int requestId, long managerRequestId,
- @NonNull IMediaRouter2 router, @NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+ private void requestCreateSessionWithRouter2Locked(
+ int requestId,
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ @NonNull IMediaRouter2 router,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @Nullable Bundle sessionHints) {
final IBinder binder = router.asBinder();
final RouterRecord routerRecord = mAllRouterRecords.get(binder);
@@ -1114,9 +1176,16 @@
long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId);
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestCreateSessionWithRouter2OnHandler,
+ obtainMessage(
+ UserHandler::requestCreateSessionWithRouter2OnHandler,
routerRecord.mUserRecord.mHandler,
- uniqueRequestId, managerRequestId, routerRecord, oldSession, route,
+ uniqueRequestId,
+ managerRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ routerRecord,
+ oldSession,
+ route,
sessionHints));
}
@@ -1165,8 +1234,11 @@
}
@GuardedBy("mLock")
- private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ private void transferToRouteWithRouter2Locked(
+ @NonNull IMediaRouter2 router,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route) {
final IBinder binder = router.asBinder();
final RouterRecord routerRecord = mAllRouterRecords.get(binder);
@@ -1191,9 +1263,16 @@
routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID)));
} else {
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::transferToRouteOnHandler,
+ obtainMessage(
+ UserHandler::transferToRouteOnHandler,
routerRecord.mUserRecord.mHandler,
- DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
+ DUMMY_REQUEST_ID,
+ transferInitiatorUserHandle,
+ routerRecord.mPackageName,
+ routerRecord,
+ uniqueSessionId,
+ route,
+ RoutingSessionInfo.TRANSFER_REASON_APP));
}
}
@@ -1416,9 +1495,13 @@
}
@GuardedBy("mLock")
- private void requestCreateSessionWithManagerLocked(int requestId,
- @NonNull IMediaRouter2Manager manager, @NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route) {
+ private void requestCreateSessionWithManagerLocked(
+ int requestId,
+ @NonNull IMediaRouter2Manager manager,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
if (managerRecord == null) {
return;
@@ -1464,9 +1547,16 @@
// Before requesting to the provider, get session hints from the media router.
// As a return, media router will request to create a session.
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestRouterCreateSessionOnHandler,
+ obtainMessage(
+ UserHandler::requestRouterCreateSessionOnHandler,
routerRecord.mUserRecord.mHandler,
- uniqueRequestId, routerRecord, managerRecord, oldSession, route));
+ uniqueRequestId,
+ routerRecord,
+ managerRecord,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName));
}
@GuardedBy("mLock")
@@ -1521,9 +1611,14 @@
}
@GuardedBy("mLock")
- private void transferToRouteWithManagerLocked(int requestId,
+ private void transferToRouteWithManagerLocked(
+ int requestId,
@NonNull IMediaRouter2Manager manager,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1541,9 +1636,16 @@
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::transferToRouteOnHandler,
+ obtainMessage(
+ UserHandler::transferToRouteOnHandler,
managerRecord.mUserRecord.mHandler,
- uniqueRequestId, routerRecord, uniqueSessionId, route));
+ uniqueRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ routerRecord,
+ uniqueSessionId,
+ route,
+ transferReason));
}
@GuardedBy("mLock")
@@ -1850,6 +1952,19 @@
}
}
+ public void notifySessionCreated(int requestId, @NonNull RoutingSessionInfo sessionInfo) {
+ try {
+ mRouter.notifySessionCreated(
+ requestId, maybeClearTransferInitiatorIdentity(sessionInfo));
+ } catch (RemoteException ex) {
+ Slog.w(
+ TAG,
+ "Failed to notify router of the session creation."
+ + " Router probably died.",
+ ex);
+ }
+ }
+
/**
* Sends the corresponding router an update for the given session.
*
@@ -1857,12 +1972,27 @@
*/
public void notifySessionInfoChanged(RoutingSessionInfo sessionInfo) {
try {
- mRouter.notifySessionInfoChanged(sessionInfo);
+ mRouter.notifySessionInfoChanged(maybeClearTransferInitiatorIdentity(sessionInfo));
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
}
}
+ private RoutingSessionInfo maybeClearTransferInitiatorIdentity(
+ @NonNull RoutingSessionInfo sessionInfo) {
+ UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
+ String transferInitiatorPackageName = sessionInfo.getTransferInitiatorPackageName();
+
+ if (!Objects.equals(UserHandle.of(mUserRecord.mUserId), transferInitiatorUserHandle)
+ || !Objects.equals(mPackageName, transferInitiatorPackageName)) {
+ return new RoutingSessionInfo.Builder(sessionInfo)
+ .setTransferInitiator(null, null)
+ .build();
+ }
+
+ return sessionInfo;
+ }
+
/**
* Returns a filtered copy of {@code routes} that contains only the routes that are {@link
* MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record.
@@ -1921,6 +2051,7 @@
String indent = prefix + " ";
pw.println(indent + "mOwnerPackageName=" + mOwnerPackageName);
+ pw.println(indent + "mTargetPackageName=" + mTargetPackageName);
pw.println(indent + "mManagerId=" + mManagerId);
pw.println(indent + "mOwnerUid=" + mOwnerUid);
pw.println(indent + "mOwnerPid=" + mOwnerPid);
@@ -2134,7 +2265,6 @@
indexOfRouteProviderInfoByUniqueId(provider.getUniqueId(), mLastProviderInfos);
MediaRoute2ProviderInfo oldInfo =
providerInfoIndex == -1 ? null : mLastProviderInfos.get(providerInfoIndex);
- MediaRouter2ServiceImpl mediaRouter2Service = mServiceRef.get();
if (oldInfo == newInfo) {
// Nothing to do.
@@ -2307,9 +2437,14 @@
return -1;
}
- private void requestRouterCreateSessionOnHandler(long uniqueRequestId,
- @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord,
- @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
+ private void requestRouterCreateSessionOnHandler(
+ long uniqueRequestId,
+ @NonNull RouterRecord routerRecord,
+ @NonNull ManagerRecord managerRecord,
+ @NonNull RoutingSessionInfo oldSession,
+ @NonNull MediaRoute2Info route,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
try {
if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission()) {
// The router lacks permission to modify system routing, so we hide system
@@ -2317,7 +2452,11 @@
route = mSystemProvider.getDefaultRoute();
}
routerRecord.mRouter.requestCreateSessionByManager(
- uniqueRequestId, oldSession, route);
+ uniqueRequestId,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
} catch (RemoteException ex) {
Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: "
+ "Failed to request. Router probably died.", ex);
@@ -2326,10 +2465,15 @@
}
}
- private void requestCreateSessionWithRouter2OnHandler(long uniqueRequestId,
- long managerRequestId, @NonNull RouterRecord routerRecord,
+ private void requestCreateSessionWithRouter2OnHandler(
+ long uniqueRequestId,
+ long managerRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ @NonNull RouterRecord routerRecord,
@NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+ @NonNull MediaRoute2Info route,
+ @Nullable Bundle sessionHints) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
@@ -2345,8 +2489,19 @@
managerRequestId, oldSession, route);
mSessionCreationRequests.add(request);
- provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName,
- route.getOriginalId(), sessionHints);
+ int transferReason = RoutingSessionInfo.TRANSFER_REASON_APP;
+ if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) {
+ transferReason = RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST;
+ }
+
+ provider.requestCreateSession(
+ uniqueRequestId,
+ routerRecord.mPackageName,
+ route.getOriginalId(),
+ sessionHints,
+ transferReason,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// routerRecord can be null if the session is system's or RCN.
@@ -2386,9 +2541,14 @@
}
// routerRecord can be null if the session is system's or RCN.
- private void transferToRouteOnHandler(long uniqueRequestId,
+ private void transferToRouteOnHandler(
+ long uniqueRequestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
@Nullable RouterRecord routerRecord,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId,
+ @NonNull MediaRoute2Info route,
+ @RoutingSessionInfo.TransferReason int transferReason) {
if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
"transferring to")) {
return;
@@ -2399,8 +2559,13 @@
if (provider == null) {
return;
}
- provider.transferToRoute(uniqueRequestId, getOriginalId(uniqueSessionId),
- route.getOriginalId());
+ provider.transferToRoute(
+ uniqueRequestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ getOriginalId(uniqueSessionId),
+ route.getOriginalId(),
+ transferReason);
}
// routerRecord is null if and only if the session is created without the request, which
@@ -2535,10 +2700,8 @@
// session info from them.
sessionInfo = mSystemProvider.getDefaultSessionInfo();
}
- notifySessionCreatedToRouter(
- matchingRequest.mRouterRecord,
- toOriginalRequestId(uniqueRequestId),
- sessionInfo);
+ matchingRequest.mRouterRecord.notifySessionCreated(
+ toOriginalRequestId(uniqueRequestId), sessionInfo);
}
private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -2646,16 +2809,6 @@
return true;
}
- private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord,
- int requestId, @NonNull RoutingSessionInfo sessionInfo) {
- try {
- routerRecord.mRouter.notifySessionCreated(requestId, sessionInfo);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify router of the session creation."
- + " Router probably died.", ex);
- }
- }
-
private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord,
int requestId) {
try {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index e562b3f..7dd1314 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -461,11 +461,24 @@
// Binder call
@Override
- public void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
- long managerRequestId, RoutingSessionInfo oldSession,
- MediaRoute2Info route, Bundle sessionHints) {
- mService2.requestCreateSessionWithRouter2(router, requestId, managerRequestId,
- oldSession, route, sessionHints);
+ public void requestCreateSessionWithRouter2(
+ IMediaRouter2 router,
+ int requestId,
+ long managerRequestId,
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ Bundle sessionHints,
+ @Nullable UserHandle transferInitiatorUserHandle,
+ @Nullable String transferInitiatorPackageName) {
+ mService2.requestCreateSessionWithRouter2(
+ router,
+ requestId,
+ managerRequestId,
+ oldSession,
+ route,
+ sessionHints,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// Binder call
@@ -580,9 +593,20 @@
// Binder call
@Override
- public void requestCreateSessionWithManager(IMediaRouter2Manager manager,
- int requestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
- mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route);
+ public void requestCreateSessionWithManager(
+ IMediaRouter2Manager manager,
+ int requestId,
+ RoutingSessionInfo oldSession,
+ MediaRoute2Info route,
+ UserHandle transferInitiatorUserHandle,
+ String transferInitiatorPackageName) {
+ mService2.requestCreateSessionWithManager(
+ manager,
+ requestId,
+ oldSession,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// Binder call
@@ -601,9 +625,20 @@
// Binder call
@Override
- public void transferToRouteWithManager(IMediaRouter2Manager manager, int requestId,
- String sessionId, MediaRoute2Info route) {
- mService2.transferToRouteWithManager(manager, requestId, sessionId, route);
+ public void transferToRouteWithManager(
+ IMediaRouter2Manager manager,
+ int requestId,
+ String sessionId,
+ MediaRoute2Info route,
+ UserHandle transferInitiatorUserHandle,
+ String transferInitiatorPackageName) {
+ mService2.transferToRouteWithManager(
+ manager,
+ requestId,
+ sessionId,
+ route,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
// Binder call
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index b424c20..07b333a 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.app.ForegroundServiceDelegationOptions;
import android.media.MediaController2;
import android.media.Session2CommandGroup;
import android.media.Session2Token;
@@ -89,6 +90,12 @@
}
@Override
+ public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
+ // TODO: Implement when MediaSession2 knows about its owner pid.
+ return null;
+ }
+
+ @Override
public boolean isSystemPriority() {
// System priority session is currently only allowed for telephony, so it's OK to stick to
// the media1 API at this moment.
@@ -217,7 +224,8 @@
synchronized (mLock) {
service = mService;
}
- service.onSessionPlaybackStateChanged(MediaSession2Record.this, playbackActive);
+ service.onSessionPlaybackStateChanged(
+ MediaSession2Record.this, playbackActive, /* playbackState= */ null);
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 994d3ca..cce66e2 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -29,6 +29,7 @@
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ForegroundServiceDelegationOptions;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
@@ -182,6 +183,8 @@
private final Context mContext;
private final boolean mVolumeAdjustmentForRemoteGroupSessions;
+ private final ForegroundServiceDelegationOptions mForegroundServiceDelegationOptions;
+
private final Object mLock = new Object();
private final CopyOnWriteArrayList<ISessionControllerCallbackHolder>
mControllerCallbackHolders = new CopyOnWriteArrayList<>();
@@ -244,10 +247,32 @@
mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
+ mForegroundServiceDelegationOptions = createForegroundServiceDelegationOptions();
+
// May throw RemoteException if the session app is killed.
mSessionCb.mCb.asBinder().linkToDeath(this, 0);
}
+ private ForegroundServiceDelegationOptions createForegroundServiceDelegationOptions() {
+ return new ForegroundServiceDelegationOptions.Builder()
+ .setClientPid(mOwnerPid)
+ .setClientUid(getUid())
+ .setClientPackageName(getPackageName())
+ .setClientAppThread(null)
+ .setSticky(false)
+ .setClientInstanceName(
+ "MediaSessionFgsDelegate_"
+ + getUid()
+ + "_"
+ + mOwnerPid
+ + "_"
+ + getPackageName())
+ .setForegroundServiceTypes(0)
+ .setDelegationService(
+ ForegroundServiceDelegationOptions.DELEGATION_SERVICE_MEDIA_PLAYBACK)
+ .build();
+ }
+
/**
* Get the session binder for the {@link MediaSession}.
*
@@ -681,6 +706,11 @@
return mPackageName + "/" + mTag + " (userId=" + mUserId + ")";
}
+ @Override
+ public ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions() {
+ return mForegroundServiceDelegationOptions;
+ }
+
private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
final String callingOpPackageName, final int callingPid, final int callingUid,
final boolean asSystemService, final boolean useSuggested,
@@ -1273,7 +1303,7 @@
final long token = Binder.clearCallingIdentity();
try {
mService.onSessionPlaybackStateChanged(
- MediaSessionRecord.this, shouldUpdatePriority);
+ MediaSessionRecord.this, shouldUpdatePriority, mPlaybackState);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 8f01f02..99c8ea9 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -16,7 +16,9 @@
package com.android.server.media;
+import android.app.ForegroundServiceDelegationOptions;
import android.media.AudioManager;
+import android.media.session.PlaybackState;
import android.os.ResultReceiver;
import android.view.KeyEvent;
@@ -51,6 +53,15 @@
int getUserId();
/**
+ * Get the {@link ForegroundServiceDelegationOptions} needed for notifying activity manager
+ * service with changes in the {@link PlaybackState} for this session.
+ *
+ * @return the {@link ForegroundServiceDelegationOptions} needed for notifying the activity
+ * manager service with changes in the {@link PlaybackState} for this session.
+ */
+ ForegroundServiceDelegationOptions getForegroundServiceDelegationOptions();
+
+ /**
* Check if this session has system priority and should receive media buttons before any other
* sessions.
*
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 2c59511..2affdfc 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -29,6 +29,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ForegroundServiceDelegationOptions;
import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -59,6 +61,7 @@
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -86,12 +89,10 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.media.flags.Flags;
-import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.Watchdog.Monitor;
-import com.android.server.am.ActivityManagerLocal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -143,7 +144,7 @@
private KeyguardManager mKeyguardManager;
private AudioManager mAudioManager;
private boolean mHasFeatureLeanback;
- private ActivityManagerLocal mActivityManagerLocal;
+ private ActivityManagerInternal mActivityManagerInternal;
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -228,7 +229,7 @@
NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED);
mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter);
- mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
}
@Override
@@ -285,7 +286,8 @@
}
user.mPriorityStack.onSessionActiveStateChanged(record);
}
-
+ setForegroundServiceAllowance(
+ record, /* allowRunningInForeground= */ record.isActive());
mHandler.postSessionsChanged(record);
}
}
@@ -371,8 +373,10 @@
}
}
- void onSessionPlaybackStateChanged(MediaSessionRecordImpl record,
- boolean shouldUpdatePriority) {
+ void onSessionPlaybackStateChanged(
+ MediaSessionRecordImpl record,
+ boolean shouldUpdatePriority,
+ @Nullable PlaybackState playbackState) {
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
if (user == null || !user.mPriorityStack.contains(record)) {
@@ -380,6 +384,10 @@
return;
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
+ if (playbackState != null) {
+ setForegroundServiceAllowance(
+ record, playbackState.shouldAllowServiceToRunInForeground());
+ }
}
}
@@ -543,27 +551,51 @@
}
session.close();
+ setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
mHandler.postSessionsChanged(session);
}
+ private void setForegroundServiceAllowance(
+ MediaSessionRecordImpl record, boolean allowRunningInForeground) {
+ if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+ return;
+ }
+ ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
+ record.getForegroundServiceDelegationOptions();
+ if (foregroundServiceDelegationOptions == null) {
+ // This record doesn't support FGS delegation. In practice, this is MediaSession2.
+ return;
+ }
+ if (allowRunningInForeground) {
+ mActivityManagerInternal.startForegroundServiceDelegate(
+ foregroundServiceDelegationOptions, /* connection= */ null);
+ } else {
+ mActivityManagerInternal.stopForegroundServiceDelegate(
+ foregroundServiceDelegationOptions);
+ }
+ }
+
void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
int callingPid, int callingUid, String callingPackage, String reason) {
final long token = Binder.clearCallingIdentity();
try {
MediaServerUtils.enforcePackageName(mContext, callingPackage, callingUid);
if (targetUid != callingUid) {
- boolean canAllowWhileInUse = mActivityManagerLocal
- .canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
- boolean canStartFgs = canAllowWhileInUse
- || mActivityManagerLocal.canStartForegroundService(callingPid, callingUid,
- callingPackage);
+ boolean canAllowWhileInUse =
+ mActivityManagerInternal.canAllowWhileInUsePermissionInFgs(
+ callingPid, callingUid, callingPackage);
+ boolean canStartFgs =
+ canAllowWhileInUse
+ || mActivityManagerInternal.canStartForegroundService(
+ callingPid, callingUid, callingPackage);
Log.i(TAG, "tempAllowlistTargetPkgIfPossible callingPackage:"
+ callingPackage + " targetPackage:" + targetPackage
+ " reason:" + reason
+ (canAllowWhileInUse ? " [WIU]" : "")
+ (canStartFgs ? " [FGS]" : ""));
if (canAllowWhileInUse) {
- mActivityManagerLocal.tempAllowWhileInUsePermissionInFgs(targetUid,
+ mActivityManagerInternal.tempAllowWhileInUsePermissionInFgs(
+ targetUid,
MediaSessionDeviceConfig
.getMediaSessionCallbackFgsWhileInUseTempAllowDurationMs());
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 9d151c2..f7210dd 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -26,6 +27,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.MediaRouter2Utils;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -39,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.media.flags.Flags;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -79,6 +82,10 @@
@GuardedBy("mRequestLock")
private volatile SessionCreationRequest mPendingSessionCreationRequest;
+ private final Object mTransferLock = new Object();
+ @GuardedBy("mTransferLock")
+ @Nullable private volatile SessionCreationRequest mPendingTransferRequest;
+
SystemMediaRoute2Provider(Context context, UserHandle user) {
super(COMPONENT_NAME);
mIsSystemRouteProvider = true;
@@ -146,17 +153,30 @@
}
@Override
- public void requestCreateSession(long requestId, String packageName, String routeId,
- Bundle sessionHints) {
+ public void requestCreateSession(
+ long requestId,
+ String packageName,
+ String routeId,
+ Bundle sessionHints,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
// Assume a router without MODIFY_AUDIO_ROUTING permission can't request with
// a route ID different from the default route ID. The service should've filtered.
if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo);
return;
}
- if (TextUtils.equals(routeId, mSelectedRouteId)) {
- mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
- return;
+
+ if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ if (TextUtils.equals(routeId, mSelectedRouteId)) {
+ RoutingSessionInfo currentSessionInfo;
+ synchronized (mLock) {
+ currentSessionInfo = mSessionInfos.get(0);
+ }
+ mCallback.onSessionCreated(this, requestId, currentSessionInfo);
+ return;
+ }
}
synchronized (mRequestLock) {
@@ -165,10 +185,23 @@
mCallback.onRequestFailed(this, mPendingSessionCreationRequest.mRequestId,
MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
}
- mPendingSessionCreationRequest = new SessionCreationRequest(requestId, routeId);
+ mPendingSessionCreationRequest =
+ new SessionCreationRequest(
+ requestId,
+ routeId,
+ RoutingSessionInfo.TRANSFER_REASON_FALLBACK,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
}
- transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
+ // Only unprivileged routers call this method, therefore we use TRANSFER_REASON_APP.
+ transferToRoute(
+ requestId,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName,
+ SYSTEM_SESSION_ID,
+ routeId,
+ transferReason);
}
@Override
@@ -193,12 +226,31 @@
}
@Override
- public void transferToRoute(long requestId, String sessionId, String routeId) {
+ public void transferToRoute(
+ long requestId,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName,
+ String sessionId,
+ String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason) {
if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
// The currently selected route is the default route.
Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT);
return;
}
+
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ synchronized (mTransferLock) {
+ mPendingTransferRequest =
+ new SessionCreationRequest(
+ requestId,
+ routeId,
+ transferReason,
+ transferInitiatorUserHandle,
+ transferInitiatorPackageName);
+ }
+ }
+
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
boolean isAvailableDeviceRoute =
mDeviceRouteController.getAvailableRoutes().stream()
@@ -218,6 +270,11 @@
mDeviceRouteController.transferTo(null);
mBluetoothRouteController.transferTo(routeId);
}
+
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
+ && updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
}
@Override
@@ -322,9 +379,11 @@
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
MediaRoute2Info selectedRoute = selectedDeviceRoute;
MediaRoute2Info selectedBtRoute = mBluetoothRouteController.getSelectedRoute();
+ List<String> transferableRoutes = new ArrayList<>();
+
if (selectedBtRoute != null) {
selectedRoute = selectedBtRoute;
- builder.addTransferableRoute(selectedDeviceRoute.getId());
+ transferableRoutes.add(selectedDeviceRoute.getId());
}
mSelectedRouteId = selectedRoute.getId();
mDefaultRoute =
@@ -337,12 +396,54 @@
for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) {
String routeId = route.getId();
if (!mSelectedRouteId.equals(routeId)) {
- builder.addTransferableRoute(routeId);
+ transferableRoutes.add(routeId);
}
}
}
for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) {
- builder.addTransferableRoute(route.getId());
+ transferableRoutes.add(route.getId());
+ }
+
+ for (String route : transferableRoutes) {
+ builder.addTransferableRoute(route);
+ }
+
+ if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+ int transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK;
+ UserHandle transferInitiatorUserHandle = null;
+ String transferInitiatorPackageName = null;
+
+ if (oldSessionInfo != null
+ && containsSelectedRouteWithId(oldSessionInfo, selectedRoute.getId())) {
+ transferReason = oldSessionInfo.getTransferReason();
+ transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle();
+ transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName();
+ }
+
+ synchronized (mTransferLock) {
+ if (mPendingTransferRequest != null) {
+ boolean isTransferringToTheSelectedRoute =
+ mPendingTransferRequest.isTargetRoute(selectedRoute);
+ boolean canBePotentiallyTransferred =
+ mPendingTransferRequest.isInsideOfRoutesList(transferableRoutes);
+
+ if (isTransferringToTheSelectedRoute) {
+ transferReason = mPendingTransferRequest.mTransferReason;
+ transferInitiatorUserHandle =
+ mPendingTransferRequest.mTransferInitiatorUserHandle;
+ transferInitiatorPackageName =
+ mPendingTransferRequest.mTransferInitiatorPackageName;
+
+ mPendingTransferRequest = null;
+ } else if (!canBePotentiallyTransferred) {
+ mPendingTransferRequest = null;
+ }
+ }
+ }
+
+ builder.setTransferReason(transferReason)
+ .setTransferInitiator(
+ transferInitiatorUserHandle, transferInitiatorPackageName);
}
RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
@@ -424,6 +525,22 @@
return false;
}
+ private boolean containsSelectedRouteWithId(
+ @Nullable RoutingSessionInfo sessionInfo, @NonNull String selectedRouteId) {
+ if (sessionInfo == null) {
+ return false;
+ }
+
+ List<String> selectedRoutes = sessionInfo.getSelectedRoutes();
+
+ if (selectedRoutes.size() != 1) {
+ throw new IllegalStateException("Selected routes list should contain only 1 route id.");
+ }
+
+ String oldSelectedRouteId = MediaRouter2Utils.getOriginalId(selectedRoutes.get(0));
+ return oldSelectedRouteId != null && oldSelectedRouteId.equals(selectedRouteId);
+ }
+
void publishProviderState() {
updateProviderState();
notifyProviderState();
@@ -452,12 +569,47 @@
}
private static class SessionCreationRequest {
- final long mRequestId;
- final String mRouteId;
+ private final long mRequestId;
+ @NonNull private final String mRouteId;
- SessionCreationRequest(long requestId, String routeId) {
- this.mRequestId = requestId;
- this.mRouteId = routeId;
+ @RoutingSessionInfo.TransferReason private final int mTransferReason;
+
+ @NonNull private final UserHandle mTransferInitiatorUserHandle;
+ @NonNull private final String mTransferInitiatorPackageName;
+
+ SessionCreationRequest(
+ long requestId,
+ @NonNull String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
+ mRequestId = requestId;
+ mRouteId = routeId;
+ mTransferReason = transferReason;
+ mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+ mTransferInitiatorPackageName = transferInitiatorPackageName;
+ }
+
+ private boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
+ if (route2Info == null) {
+ return false;
+ }
+
+ return isTargetRoute(route2Info.getId());
+ }
+
+ private boolean isTargetRoute(@Nullable String routeId) {
+ return mRouteId.equals(routeId);
+ }
+
+ private boolean isInsideOfRoutesList(@NonNull List<String> routesList) {
+ for (String routeId : routesList) {
+ if (isTargetRoute(routeId)) {
+ return true;
+ }
+ }
+
+ return false;
}
}
diff --git a/services/core/java/com/android/server/media/TEST_MAPPING b/services/core/java/com/android/server/media/TEST_MAPPING
index 1b49093..b3e5b9e 100644
--- a/services/core/java/com/android/server/media/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "CtsMediaBetterTogetherTestCases"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "MediaRouterServiceTests"
+ }
]
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 85731651..4d19ead 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -42,6 +42,7 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.Keep;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.RingBuffer;
import com.android.server.am.ProcessList;
@@ -693,6 +694,7 @@
* Note: This class needs to be public for RingBuffer class to be able to create
* new instances of this.
*/
+ @Keep
public static final class Data {
public int type;
public long timeStamp;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 46e7041..e4e48bd 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4090,6 +4090,7 @@
}
fout.decreaseIndent();
+ fout.println();
fout.println("Admin restricted uids for metered data:");
fout.increaseIndent();
size = mMeteredRestrictedUids.size();
@@ -4099,6 +4100,7 @@
}
fout.decreaseIndent();
+ fout.println();
fout.println("Network to interfaces:");
fout.increaseIndent();
for (int i = 0; i < mNetworkToIfaces.size(); ++i) {
@@ -4108,6 +4110,10 @@
fout.decreaseIndent();
fout.println();
+ fout.print("Active notifications: ");
+ fout.println(mActiveNotifs);
+
+ fout.println();
mStatLogger.dump(fout);
mLogger.dumpLogs(fout);
@@ -6672,7 +6678,7 @@
* Build unique tag that identifies an active {@link NetworkPolicy}
* notification of a specific type, like {@link #TYPE_LIMIT}.
*/
- private String buildNotificationTag(NetworkPolicy policy, int type) {
+ private static String buildNotificationTag(NetworkPolicy policy, int type) {
return TAG + ":" + policy.template.hashCode() + ":" + type;
}
@@ -6683,5 +6689,10 @@
public int getId() {
return mId;
}
+
+ @Override
+ public String toString() {
+ return mTag;
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 2da1a68..66e61c0 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -234,7 +234,7 @@
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
try {
- inm.removeAutomaticZenRules(pkgName);
+ inm.removeAutomaticZenRules(pkgName, /* fromUser= */ false);
inm.setNotificationPolicyAccessGranted(pkgName, false);
} catch (Exception e) {
Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 8855666..71a6b5e 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -109,7 +109,6 @@
if (origin == ZenModeConfig.UPDATE_ORIGIN_INIT
|| origin == ZenModeConfig.UPDATE_ORIGIN_INIT_USER
|| origin == ZenModeConfig.UPDATE_ORIGIN_USER
- || origin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
|| !mPowerManager.isInteractive()) {
unregisterScreenOffReceiver();
updateNightModeImmediately(useNightMode);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 75d3dce..7fbc085 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,7 +16,9 @@
package com.android.server.notification;
+import static android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS;
import static android.Manifest.permission.RECEIVE_SENSITIVE_NOTIFICATIONS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -67,6 +69,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.app.Flags.lifetimeExtensionRefactor;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
@@ -210,6 +214,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
@@ -3369,9 +3374,7 @@
.setChannelName(r.getChannel().getName().toString())
.setPostedTimeMs(System.currentTimeMillis())
.setTitle(getHistoryTitle(r.getNotification()))
- .setText(getHistoryText(
- r.getSbn().getPackageContext(getContext()),
- r.getNotification()))
+ .setText(getHistoryText(r.getNotification()))
.setIcon(r.getNotification().getSmallIcon())
.build());
}
@@ -3414,12 +3417,11 @@
/**
* Returns the appropriate substring for this notification based on the style of notification.
*/
- private String getHistoryText(Context appContext, Notification n) {
+ private String getHistoryText(Notification n) {
CharSequence text = null;
if (n.extras != null) {
text = n.extras.getCharSequence(EXTRA_TEXT);
-
- Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
+ Notification.Builder nb = Notification.Builder.recoverBuilder(getContext(), n);
if (nb.getStyle() instanceof Notification.BigTextStyle) {
text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
@@ -5126,8 +5128,10 @@
for (int userId : mUm.getProfileIds(info.userid, false)) {
try {
int uid = getUidForPackageAndUser(pkg, UserHandle.of(userId));
- VersionedPackage vp = new VersionedPackage(pkg, uid);
- nlf.addPackage(vp);
+ if (uid != INVALID_UID) {
+ VersionedPackage vp = new VersionedPackage(pkg, uid);
+ nlf.addPackage(vp);
+ }
} catch (Exception e) {
// pkg doesn't exist on that user; skip
}
@@ -5343,13 +5347,14 @@
}
@Override
- public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
+ public void setZenMode(int mode, Uri conditionId, String reason, boolean fromUser) {
enforceSystemOrSystemUI("INotificationManager.setZenMode");
final int callingUid = Binder.getCallingUid();
final long identity = Binder.clearCallingIdentity();
+ enforceUserOriginOnlyFromSystem(fromUser, "setZenMode");
+
try {
- mZenModeHelper.setManualZenMode(mode, conditionId,
- ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, // Checked by enforce()
+ mZenModeHelper.setManualZenMode(mode, conditionId, computeZenOrigin(fromUser),
reason, /* caller= */ null, callingUid);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -5380,8 +5385,9 @@
}
@Override
- public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) {
- validateAutomaticZenRule(automaticZenRule);
+ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg,
+ boolean fromUser) {
+ validateAutomaticZenRule(/* updateId= */ null, automaticZenRule);
checkCallerIsSameApp(pkg);
if (automaticZenRule.getZenPolicy() != null
&& automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
@@ -5389,6 +5395,7 @@
+ "INTERRUPTION_FILTER_PRIORITY filters");
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
+ enforceUserOriginOnlyFromSystem(fromUser, "addAutomaticZenRule");
// If the calling app is the system (from any user), take the package name from the
// rule's owner rather than from the caller's package.
@@ -5400,72 +5407,95 @@
}
return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if creating a rule
- // manually in Settings).
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
- "addAutomaticZenRule", Binder.getCallingUid());
+ computeZenOrigin(fromUser), "addAutomaticZenRule", Binder.getCallingUid());
}
@Override
- public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule) {
- validateAutomaticZenRule(automaticZenRule);
+ public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule,
+ boolean fromUser) throws RemoteException {
+ validateAutomaticZenRule(id, automaticZenRule);
enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
+ enforceUserOriginOnlyFromSystem(fromUser, "updateAutomaticZenRule");
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if updating a rule
- // manually in Settings).
return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
- "updateAutomaticZenRule", Binder.getCallingUid());
+ computeZenOrigin(fromUser), "updateAutomaticZenRule", Binder.getCallingUid());
}
- private void validateAutomaticZenRule(AutomaticZenRule rule) {
+ private void validateAutomaticZenRule(@Nullable String updateId, AutomaticZenRule rule) {
Objects.requireNonNull(rule, "automaticZenRule is null");
Objects.requireNonNull(rule.getName(), "Name is null");
rule.validate();
- if (rule.getOwner() == null
- && rule.getConfigurationActivity() == null) {
- throw new NullPointerException(
- "Rule must have a conditionproviderservice and/or configuration activity");
+
+ // Implicit rules have no ConditionProvider or Activity. We allow the user to customize
+ // them (via Settings), but not the owner app. Should the app want to start using it as
+ // a "normal" rule, it must provide a CP/ConfigActivity too.
+ if (android.app.Flags.modesApi()) {
+ boolean isImplicitRuleUpdateFromSystem = updateId != null
+ && ZenModeHelper.isImplicitRuleId(updateId)
+ && isCallerSystemOrSystemUi();
+ if (!isImplicitRuleUpdateFromSystem
+ && rule.getOwner() == null
+ && rule.getConfigurationActivity() == null) {
+ throw new NullPointerException(
+ "Rule must have a ConditionProviderService and/or configuration "
+ + "activity");
+ }
+ } else {
+ if (rule.getOwner() == null && rule.getConfigurationActivity() == null) {
+ throw new NullPointerException(
+ "Rule must have a ConditionProviderService and/or configuration "
+ + "activity");
+ }
}
Objects.requireNonNull(rule.getConditionId(), "ConditionId is null");
if (android.app.Flags.modesApi()) {
+ if (isCallerSystemOrSystemUi()) {
+ return; // System callers can use any type.
+ }
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getUserId(uid);
+
if (rule.getType() == AutomaticZenRule.TYPE_MANAGED) {
- int uid = Binder.getCallingUid();
boolean isDeviceOwner = Binder.withCleanCallingIdentity(
() -> mDpm.isActiveDeviceOwner(uid));
if (!isDeviceOwner) {
throw new IllegalArgumentException(
"Only Device Owners can use AutomaticZenRules with TYPE_MANAGED");
}
+ } else if (rule.getType() == AutomaticZenRule.TYPE_BEDTIME) {
+ String wellbeingPackage = getContext().getResources().getString(
+ com.android.internal.R.string.config_systemWellbeing);
+ boolean isCallerWellbeing = !TextUtils.isEmpty(wellbeingPackage)
+ && mPackageManagerInternal.isSameApp(wellbeingPackage, uid, userId);
+ if (!isCallerWellbeing) {
+ throw new IllegalArgumentException(
+ "Only the 'Wellbeing' package can use AutomaticZenRules with "
+ + "TYPE_BEDTIME");
+ }
}
}
}
@Override
- public boolean removeAutomaticZenRule(String id) throws RemoteException {
+ public boolean removeAutomaticZenRule(String id, boolean fromUser) throws RemoteException {
Objects.requireNonNull(id, "Id is null");
// Verify that they can modify zen rules.
enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
+ enforceUserOriginOnlyFromSystem(fromUser, "removeAutomaticZenRule");
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if removing a rule
- // manually in Settings).
- return mZenModeHelper.removeAutomaticZenRule(id,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ return mZenModeHelper.removeAutomaticZenRule(id, computeZenOrigin(fromUser),
"removeAutomaticZenRule", Binder.getCallingUid());
}
@Override
- public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
+ public boolean removeAutomaticZenRules(String packageName, boolean fromUser)
+ throws RemoteException {
Objects.requireNonNull(packageName, "Package name is null");
enforceSystemOrSystemUI("removeAutomaticZenRules");
+ enforceUserOriginOnlyFromSystem(fromUser, "removeAutomaticZenRules");
- return mZenModeHelper.removeAutomaticZenRules(packageName,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ return mZenModeHelper.removeAutomaticZenRules(packageName, computeZenOrigin(fromUser),
packageName + "|removeAutomaticZenRules", Binder.getCallingUid());
}
@@ -5484,22 +5514,41 @@
condition.validate();
enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
+ boolean fromUser = (condition.source == Condition.SOURCE_USER_ACTION);
- // TODO: b/308670715: Distinguish origin properly (e.g. USER if toggling a rule
- // manually in Settings).
- mZenModeHelper.setAutomaticZenRuleState(id, condition,
- isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ mZenModeHelper.setAutomaticZenRuleState(id, condition, computeZenOrigin(fromUser),
Binder.getCallingUid());
}
+ @ZenModeConfig.ConfigChangeOrigin
+ private int computeZenOrigin(boolean fromUser) {
+ // "fromUser" is introduced with MODES_API, so only consider it in that case.
+ // (Non-MODES_API behavior should also not depend at all on UPDATE_ORIGIN_USER).
+ if (android.app.Flags.modesApi() && fromUser) {
+ return ZenModeConfig.UPDATE_ORIGIN_USER;
+ } else if (isCallerSystemOrSystemUi()) {
+ return ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI;
+ } else {
+ return ZenModeConfig.UPDATE_ORIGIN_APP;
+ }
+ }
+
+ private void enforceUserOriginOnlyFromSystem(boolean fromUser, String method) {
+ if (android.app.Flags.modesApi()
+ && fromUser
+ && !isCallerSystemOrSystemUiOrShell()) {
+ throw new SecurityException(TextUtils.formatSimple(
+ "Calling %s with fromUser == true is only allowed for system", method));
+ }
+ }
+
@Override
- public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
+ public void setInterruptionFilter(String pkg, int filter, boolean fromUser) {
enforcePolicyAccess(pkg, "setInterruptionFilter");
final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
+ enforceUserOriginOnlyFromSystem(fromUser, "setInterruptionFilter");
if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
@@ -5508,9 +5557,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- mZenModeHelper.setManualZenMode(zen, null,
- isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
+ mZenModeHelper.setManualZenMode(zen, null, computeZenOrigin(fromUser),
/* reason= */ "setInterruptionFilter", /* caller= */ pkg,
callingUid);
} finally {
@@ -5558,7 +5605,7 @@
private void enforceSystemOrSystemUI(String message) {
if (isCallerSystemOrPhone()) return;
- getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ getContext().enforceCallingPermission(STATUS_BAR_SERVICE,
message);
}
@@ -5567,7 +5614,7 @@
checkCallerIsSystemOrSameApp(pkg);
} catch (SecurityException e) {
getContext().enforceCallingPermission(
- android.Manifest.permission.STATUS_BAR_SERVICE,
+ STATUS_BAR_SERVICE,
message);
}
}
@@ -5599,7 +5646,8 @@
return !isCompatChangeEnabled
|| isCallerSystemOrSystemUi()
|| hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
- AssociationRequest.DEVICE_PROFILE_WATCH);
+ Set.of(AssociationRequest.DEVICE_PROFILE_WATCH,
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION));
}
private void enforcePolicyAccess(String pkg, String method) {
@@ -5825,10 +5873,11 @@
* {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd
*/
@Override
- public void setNotificationPolicy(String pkg, Policy policy) {
+ public void setNotificationPolicy(String pkg, Policy policy, boolean fromUser) {
enforcePolicyAccess(pkg, "setNotificationPolicy");
+ enforceUserOriginOnlyFromSystem(fromUser, "setNotificationPolicy");
int callingUid = Binder.getCallingUid();
- boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
+ @ZenModeConfig.ConfigChangeOrigin int origin = computeZenOrigin(fromUser);
boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
&& !canManageGlobalZenPolicy(pkg, callingUid);
@@ -5873,14 +5922,12 @@
newVisualEffects, policy.priorityConversationSenders);
if (shouldApplyAsImplicitRule) {
- mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy,
+ origin);
} else {
ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
policy);
- mZenModeHelper.setNotificationPolicy(policy,
- isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- : ZenModeConfig.UPDATE_ORIGIN_APP,
- callingUid);
+ mZenModeHelper.setNotificationPolicy(policy, origin, callingUid);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set notification policy", e);
@@ -6172,13 +6219,20 @@
@Override
public void setPrivateNotificationsAllowed(boolean allow) {
if (PackageManager.PERMISSION_GRANTED
- != getContext().checkCallingPermission(
- permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
+ != getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
throw new SecurityException(
"Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
}
if (allow != mLockScreenAllowSecureNotifications) {
mLockScreenAllowSecureNotifications = allow;
+ if (android.app.Flags.keyguardPrivateNotifications()) {
+ getContext().sendBroadcast(
+ new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED,
+ mLockScreenAllowSecureNotifications),
+ STATUS_BAR_SERVICE);
+ }
+
handleSavePolicyFile();
}
}
@@ -6186,8 +6240,7 @@
@Override
public boolean getPrivateNotificationsAllowed() {
if (PackageManager.PERMISSION_GRANTED
- != getContext().checkCallingPermission(
- permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
+ != getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
throw new SecurityException(
"Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
}
@@ -7001,12 +7054,14 @@
return false;
}
- final boolean hasBitmap = n.extras.containsKey(Notification.EXTRA_PICTURE);
+ final boolean hasBitmap = n.extras.containsKey(Notification.EXTRA_PICTURE)
+ && n.extras.getParcelable(Notification.EXTRA_PICTURE) != null;
if (hasBitmap) {
return true;
}
- final boolean hasIcon = n.extras.containsKey(Notification.EXTRA_PICTURE_ICON);
+ final boolean hasIcon = n.extras.containsKey(Notification.EXTRA_PICTURE_ICON)
+ && n.extras.getParcelable(Notification.EXTRA_PICTURE_ICON) != null;
if (hasIcon) {
return true;
}
@@ -7022,9 +7077,10 @@
if (!isBigPictureWithBitmapOrIcon(r.getNotification())) {
return;
}
- // Remove Notification object's reference to picture bitmap or URI
- r.getNotification().extras.remove(Notification.EXTRA_PICTURE);
- r.getNotification().extras.remove(Notification.EXTRA_PICTURE_ICON);
+ // Remove Notification object's reference to picture bitmap or URI. Leave the extras set to
+ // null to avoid crashing apps that came to expect them to be present but null.
+ r.getNotification().extras.putParcelable(Notification.EXTRA_PICTURE, null);
+ r.getNotification().extras.putParcelable(Notification.EXTRA_PICTURE_ICON, null);
// Make Notification silent
r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
@@ -8355,6 +8411,8 @@
boolean posted = false;
try {
posted = postNotification();
+ } catch (Exception e) {
+ Slog.e(TAG, "Error posting", e);
} finally {
if (!posted) {
mTracker.cancel();
@@ -9543,12 +9601,16 @@
}
private void scheduleListenerHintsChanged(int state) {
- mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
+ if (!Flags.notificationReduceMessagequeueUsage()) {
+ mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
+ }
mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
}
private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
- mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+ if (!Flags.notificationReduceMessagequeueUsage()) {
+ mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+ }
mHandler.obtainMessage(
MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
listenerInterruptionFilter,
@@ -9628,15 +9690,24 @@
}
protected void scheduleSendRankingUpdate() {
- if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+ if (Flags.notificationReduceMessagequeueUsage()) {
Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
sendMessage(m);
+ } else {
+ if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+ Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
+ sendMessage(m);
+ }
}
}
protected void scheduleCancelNotification(CancelNotificationRunnable cancelRunnable) {
- if (!hasCallbacks(cancelRunnable)) {
+ if (Flags.notificationReduceMessagequeueUsage()) {
sendMessage(Message.obtain(this, cancelRunnable));
+ } else {
+ if (!hasCallbacks(cancelRunnable)) {
+ sendMessage(Message.obtain(this, cancelRunnable));
+ }
}
}
@@ -9670,7 +9741,9 @@
}
public void requestSort() {
- removeMessages(MESSAGE_RANKING_SORT);
+ if (!Flags.notificationReduceMessagequeueUsage()) {
+ removeMessages(MESSAGE_RANKING_SORT);
+ }
Message msg = Message.obtain();
msg.what = MESSAGE_RANKING_SORT;
sendMessage(msg);
@@ -10575,7 +10648,7 @@
if (isCallerSystemOrPhone()) {
return true;
}
- return getContext().checkCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+ return getContext().checkCallingPermission(STATUS_BAR_SERVICE)
== PERMISSION_GRANTED;
}
@@ -10614,7 +10687,7 @@
if (isCallerSystemOrPhone()) {
return;
}
- getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ getContext().enforceCallingPermission(STATUS_BAR_SERVICE,
message);
}
@@ -10733,6 +10806,14 @@
final String key = record.getSbn().getKey();
final NotificationListenerService.Ranking ranking =
new NotificationListenerService.Ranking();
+ ArrayList<Notification.Action> smartActions = record.getSystemGeneratedSmartActions();
+ ArrayList<CharSequence> smartReplies = record.getSmartReplies();
+ if (redactSensitiveNotificationsFromUntrustedListeners()
+ && !mListeners.isUidTrusted(info.uid)
+ && mListeners.hasSensitiveContent(record)) {
+ smartActions = null;
+ smartReplies = null;
+ }
ranking.populate(
key,
rankings.size(),
@@ -10750,8 +10831,8 @@
record.isHidden(),
record.getLastAudiblyAlertedMs(),
record.getSound() != null || record.getVibration() != null,
- record.getSystemGeneratedSmartActions(),
- record.getSmartReplies(),
+ smartActions,
+ smartReplies,
record.canBubble(),
record.isTextChanged(),
record.isConversation(),
@@ -10780,7 +10861,7 @@
}
private boolean hasCompanionDevice(String pkg, @UserIdInt int userId,
- @Nullable @AssociationRequest.DeviceProfile String withDeviceProfile) {
+ @Nullable Set</* @AssociationRequest.DeviceProfile */ String> withDeviceProfiles) {
if (mCompanionManager == null) {
mCompanionManager = getCompanionManager();
}
@@ -10792,7 +10873,7 @@
try {
List<AssociationInfo> associations = mCompanionManager.getAssociations(pkg, userId);
for (AssociationInfo association : associations) {
- if (withDeviceProfile == null || withDeviceProfile.equals(
+ if (withDeviceProfiles == null || withDeviceProfiles.contains(
association.getDeviceProfile())) {
return true;
}
@@ -11501,20 +11582,16 @@
super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, userSet);
String pkgName = getPackageName(pkgOrComponent);
if (redactSensitiveNotificationsFromUntrustedListeners()) {
- try {
- int uid = mPackageManagerClient.getPackageUidAsUser(pkgName, userId);
- if (!enabled) {
- synchronized (mTrustedListenerUids) {
- mTrustedListenerUids.remove(uid);
- }
+ int uid = mPackageManagerInternal.getPackageUid(pkgName, 0, userId);
+ if (!enabled && uid >= 0) {
+ synchronized (mTrustedListenerUids) {
+ mTrustedListenerUids.remove(uid);
}
- if (enabled && isAppTrustedNotificationListenerService(uid, pkgName)) {
- synchronized (mTrustedListenerUids) {
- mTrustedListenerUids.add(uid);
- }
+ }
+ if (enabled && uid >= 0 && isAppTrustedNotificationListenerService(uid, pkgName)) {
+ synchronized (mTrustedListenerUids) {
+ mTrustedListenerUids.add(uid);
}
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "PackageManager could not find package " + pkgName, e);
}
}
@@ -11934,8 +12011,10 @@
for (final ManagedServiceInfo info : getServices()) {
boolean isTrusted = isUidTrusted(info.uid);
- boolean sendRedacted = isNewSensitive && !isTrusted;
- boolean sendOldRedacted = isOldSensitive && !isTrusted;
+ boolean sendRedacted = redactSensitiveNotificationsFromUntrustedListeners()
+ && isNewSensitive && !isTrusted;
+ boolean sendOldRedacted = redactSensitiveNotificationsFromUntrustedListeners()
+ && isOldSensitive && !isTrusted;
boolean sbnVisible = isVisibleToListener(sbn, r.getNotificationType(), info);
boolean oldSbnVisible = (oldSbn != null)
&& isVisibleToListener(oldSbn, old.getNotificationType(), info);
@@ -12034,7 +12113,7 @@
StatusBarNotification redactStatusBarNotification(StatusBarNotification sbn) {
if (!redactSensitiveNotificationsFromUntrustedListeners()) {
- return sbn;
+ throw new RuntimeException("redactStatusBarNotification called while flag is off");
}
ApplicationInfo appInfo = sbn.getNotification().extras.getParcelable(
@@ -12206,6 +12285,7 @@
public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
boolean isHiddenRankingUpdate = changedHiddenNotifications != null
&& changedHiddenNotifications.size() > 0;
+
// TODO (b/73052211): if the ranking update changed the notification type,
// cancel notifications for NLSes that can't see them anymore
for (final ManagedServiceInfo serviceInfo : getServices()) {
@@ -12229,7 +12309,6 @@
if (notifyThisListener || !isHiddenRankingUpdate) {
final NotificationRankingUpdate update = makeRankingUpdateLocked(
serviceInfo);
-
mHandler.post(() -> notifyRankingUpdate(serviceInfo, update));
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index dc0cf4e..9f3104c 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -117,7 +117,6 @@
private final NotificationManagerService mDirectService;
private final INotificationManager mBinderService;
private final PackageManager mPm;
- private NotificationChannel mChannel;
public NotificationShellCmd(NotificationManagerService service) {
mDirectService = service;
@@ -183,7 +182,13 @@
interruptionFilter = INTERRUPTION_FILTER_ALL;
}
final int filter = interruptionFilter;
- mBinderService.setInterruptionFilter(callingPackage, filter);
+ if (android.app.Flags.modesApi()) {
+ mBinderService.setInterruptionFilter(callingPackage, filter,
+ /* fromUser= */ true);
+ } else {
+ mBinderService.setInterruptionFilter(callingPackage, filter,
+ /* fromUser= */ false);
+ }
}
break;
case "allow_dnd": {
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index 87158cd..0145577 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -23,12 +23,14 @@
import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Flags;
import android.app.NotificationManager;
import android.content.pm.PackageManager;
import android.os.Process;
import android.service.notification.DNDPolicyProto;
import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
import android.service.notification.ZenModeDiff;
import android.service.notification.ZenPolicy;
import android.util.ArrayMap;
@@ -58,7 +60,7 @@
// mode change.
ZenModeEventLogger.ZenStateChanges mChangeState = new ZenModeEventLogger.ZenStateChanges();
- private PackageManager mPm;
+ private final PackageManager mPm;
ZenModeEventLogger(PackageManager pm) {
mPm = pm;
@@ -97,11 +99,11 @@
* @param newInfo ZenModeInfo after this change takes effect
* @param callingUid the calling UID associated with the change; may be used to attribute the
* change to a particular package or determine if this is a user action
- * @param fromSystemOrSystemUi whether the calling UID is either system UID or system UI
+ * @param origin The origin of the Zen change.
*/
public final void maybeLogZenChange(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
- boolean fromSystemOrSystemUi) {
- mChangeState.init(prevInfo, newInfo, callingUid, fromSystemOrSystemUi);
+ @ConfigChangeOrigin int origin) {
+ mChangeState.init(prevInfo, newInfo, callingUid, origin);
if (mChangeState.shouldLogChanges()) {
maybeReassignCallingUid();
logChanges();
@@ -124,7 +126,7 @@
// We don't consider the manual rule in the old config because if a manual rule is turning
// off with a call from system, that could easily be a user action to explicitly turn it off
if (mChangeState.getChangedRuleType() == RULE_TYPE_MANUAL) {
- if (!mChangeState.mFromSystemOrSystemUi
+ if (!mChangeState.isFromSystemOrSystemUi()
|| mChangeState.getNewManualRuleEnabler() == null) {
return;
}
@@ -136,7 +138,7 @@
// - we've determined it's not a user action
// - our current best guess is that the calling uid is system/sysui
if (mChangeState.getChangedRuleType() == RULE_TYPE_AUTOMATIC) {
- if (mChangeState.getIsUserAction() || !mChangeState.mFromSystemOrSystemUi) {
+ if (mChangeState.getIsUserAction() || !mChangeState.isFromSystemOrSystemUi()) {
return;
}
@@ -221,10 +223,10 @@
ZenModeConfig mPrevConfig, mNewConfig;
NotificationManager.Policy mPrevPolicy, mNewPolicy;
int mCallingUid = Process.INVALID_UID;
- boolean mFromSystemOrSystemUi = false;
+ @ConfigChangeOrigin int mOrigin = ZenModeConfig.UPDATE_ORIGIN_UNKNOWN;
private void init(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
- boolean fromSystemOrSystemUi) {
+ @ConfigChangeOrigin int origin) {
// previous & new may be the same -- that would indicate that zen mode hasn't changed.
mPrevZenMode = prevInfo.mZenMode;
mNewZenMode = newInfo.mZenMode;
@@ -233,7 +235,7 @@
mPrevPolicy = prevInfo.mPolicy;
mNewPolicy = newInfo.mPolicy;
mCallingUid = callingUid;
- mFromSystemOrSystemUi = fromSystemOrSystemUi;
+ mOrigin = origin;
}
/**
@@ -255,13 +257,21 @@
return true;
}
+ if (Flags.modesApi() && hasActiveRuleCountDiff()) {
+ // Rules with INTERRUPTION_FILTER_ALL were always possible but before MODES_API
+ // they were completely useless; now they can apply effects, so we want to log
+ // when they become active/inactive, even though DND itself (as in "notification
+ // blocking") is off.
+ return true;
+ }
+
// If zen mode didn't change, did the policy or number of active rules change? We only
// care about changes that take effect while zen mode is on, so make sure the current
// zen mode is not "OFF"
if (mNewZenMode == ZEN_MODE_OFF) {
return false;
}
- return hasPolicyDiff() || hasRuleCountDiff();
+ return hasPolicyDiff() || hasActiveRuleCountDiff();
}
// Does the difference in zen mode go from off to on or vice versa?
@@ -293,6 +303,16 @@
}
}
+ if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
+ // If the mode is OFF -> OFF then there cannot be any *effective* change to policy.
+ // (Note that, in theory, a policy diff is impossible since we don't merge the
+ // policies of INTERRUPTION_FILTER_ALL rules; this is a "just in case" check).
+ if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
+ Log.wtf(TAG, "Detected policy diff even though DND is OFF and not toggled");
+ }
+ return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED;
+ }
+
// zen mode didn't change; we must be here because of a policy change or rule change
if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
return ZenStateChangedEvent.DND_POLICY_CHANGED;
@@ -344,7 +364,7 @@
* Returns whether the previous config and new config have a different number of active
* automatic or manual rules.
*/
- private boolean hasRuleCountDiff() {
+ private boolean hasActiveRuleCountDiff() {
return numActiveRulesInConfig(mPrevConfig) != numActiveRulesInConfig(mNewConfig);
}
@@ -380,21 +400,27 @@
// Determine the number of (automatic & manual) rules active after the change takes place.
int getNumRulesActive() {
- // If the zen mode has turned off, that means nothing can be active.
- if (mNewZenMode == ZEN_MODE_OFF) {
- return 0;
+ if (!Flags.modesApi()) {
+ // If the zen mode has turned off, that means nothing can be active.
+ if (mNewZenMode == ZEN_MODE_OFF) {
+ return 0;
+ }
}
return numActiveRulesInConfig(mNewConfig);
}
/**
* Return our best guess as to whether the changes observed are due to a user action.
- * Note that this won't be 100% accurate as we can't necessarily distinguish between a
- * system uid call indicating "user interacted with Settings" vs "a system app changed
- * something automatically".
+ * Note that this (before {@code MODES_API}) won't be 100% accurate as we can't necessarily
+ * distinguish between a system uid call indicating "user interacted with Settings" vs "a
+ * system app changed something automatically".
*/
boolean getIsUserAction() {
- // Approach:
+ if (Flags.modesApi()) {
+ return mOrigin == ZenModeConfig.UPDATE_ORIGIN_USER;
+ }
+
+ // Approach for pre-MODES_API:
// - if manual rule turned on or off, the calling UID is system, and the new manual
// rule does not have an enabler set, guess that this is likely to be a user action.
// This may represent a system app turning on DND automatically, but we guess "user"
@@ -419,13 +445,13 @@
switch (getChangedRuleType()) {
case RULE_TYPE_MANUAL:
// TODO(b/278888961): Distinguish the automatically-turned-off state
- return mFromSystemOrSystemUi && (getNewManualRuleEnabler() == null);
+ return isFromSystemOrSystemUi() && (getNewManualRuleEnabler() == null);
case RULE_TYPE_AUTOMATIC:
for (ZenModeDiff.RuleDiff d : getChangedAutomaticRules().values()) {
if (d.wasAdded() || d.wasRemoved()) {
// If the change comes from system, a rule being added/removed indicates
// a likely user action. From an app, it's harder to know for sure.
- return mFromSystemOrSystemUi;
+ return isFromSystemOrSystemUi();
}
ZenModeDiff.FieldDiff enabled = d.getDiffForField(
ZenModeDiff.RuleDiff.FIELD_ENABLED);
@@ -455,6 +481,13 @@
return false;
}
+ boolean isFromSystemOrSystemUi() {
+ return mOrigin == ZenModeConfig.UPDATE_ORIGIN_INIT
+ || mOrigin == ZenModeConfig.UPDATE_ORIGIN_INIT_USER
+ || mOrigin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ || mOrigin == ZenModeConfig.UPDATE_ORIGIN_RESTORE_BACKUP;
+ }
+
/**
* Get the package UID associated with this change, which is just the calling UID for the
* relevant method changes. This may get reset by ZenModeEventLogger, which has access to
@@ -466,8 +499,19 @@
/**
* Convert the new policy to a DNDPolicyProto format for output in logs.
+ *
+ * <p>If {@code mNewZenMode} is {@code ZEN_MODE_OFF} (which can mean either no rules
+ * active, or only rules with {@code INTERRUPTION_FILTER_ALL} active) then this returns
+ * {@code null} (which will be mapped to a missing submessage in the proto). Although this
+ * is not the value of {@code NotificationManager#getConsolidatedNotificationPolicy()}, it
+ * makes sense for logging since that policy is not actually influencing anything.
*/
+ @Nullable
byte[] getDNDPolicyProto() {
+ if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
+ return null;
+ }
+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ProtoOutputStream proto = new ProtoOutputStream(bytes);
@@ -612,7 +656,7 @@
copy.mPrevPolicy = mPrevPolicy.copy();
copy.mNewPolicy = mNewPolicy.copy();
copy.mCallingUid = mCallingUid;
- copy.mFromSystemOrSystemUi = mFromSystemOrSystemUi;
+ copy.mOrigin = mOrigin;
return copy;
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 3f8b595..3244aff 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -134,6 +134,8 @@
private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
static final int RULE_LIMIT_PER_PACKAGE = 100;
+ private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name
+
/**
* Send new activation AutomaticZenRule statuses to apps with a min target SDK version
*/
@@ -561,7 +563,7 @@
* {@link Global#ZEN_MODE_IMPORTANT_INTERRUPTIONS}.
*/
void applyGlobalPolicyAsImplicitZenRule(String callingPkg, int callingUid,
- NotificationManager.Policy policy) {
+ NotificationManager.Policy policy, @ConfigChangeOrigin int origin) {
if (!android.app.Flags.modesApi()) {
Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
return;
@@ -579,7 +581,7 @@
}
// TODO: b/308673679 - Keep user customization of this rule!
rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
- setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP,
+ setConfigLocked(newConfig, /* triggeringComponent= */ null, origin,
"applyGlobalPolicyAsImplicitZenRule", callingUid);
}
}
@@ -653,7 +655,11 @@
}
private static String implicitRuleId(String forPackage) {
- return "implicit_" + forPackage;
+ return IMPLICIT_RULE_ID_PREFIX + forPackage;
+ }
+
+ static boolean isImplicitRuleId(@NonNull String ruleId) {
+ return ruleId.startsWith(IMPLICIT_RULE_ID_PREFIX);
}
boolean removeAutomaticZenRule(String id, @ConfigChangeOrigin int origin, String reason,
@@ -1371,12 +1377,8 @@
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
mZenMode, mConfig, mConsolidatedPolicy);
- boolean fromSystemOrSystemUi = origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
- || origin == UPDATE_ORIGIN_INIT
- || origin == UPDATE_ORIGIN_INIT_USER
- || origin == UPDATE_ORIGIN_RESTORE_BACKUP;
mZenModeEventLogger.maybeLogZenChange(prevInfo, newInfo, callingUid,
- fromSystemOrSystemUi);
+ origin);
}
}
@@ -1494,7 +1496,12 @@
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
- applyCustomPolicy(policy, automaticRule);
+ // Active rules with INTERRUPTION_FILTER_ALL are not included in consolidated
+ // policy. This is relevant in case some other active rule has a more
+ // restrictive INTERRUPTION_FILTER but a more lenient ZenPolicy!
+ if (!Flags.modesApi() || automaticRule.zenMode != Global.ZEN_MODE_OFF) {
+ applyCustomPolicy(policy, automaticRule);
+ }
if (Flags.modesApi()) {
deviceEffectsBuilder.add(automaticRule.zenDeviceEffects);
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 55d8a0f..49db7fc 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -41,3 +41,19 @@
description: "This flag controls the fix for notifications autogroup summary icon updates"
bug: "227693160"
}
+
+flag {
+ name: "sensitive_notification_app_protection"
+ namespace: "systemui"
+ description: "This flag controls the sensitive notification app protections while screen sharing"
+ bug: "312784351"
+ # Referenced in WM where WM starts before DeviceConfig
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "notification_reduce_messagequeue_usage"
+ namespace: "systemui"
+ description: "When this flag is on, NMS will no longer call removeMessage() and hasCallbacks() on Handler"
+ bug: "311051285"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 7f58e75e..e830d28 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -827,7 +827,8 @@
// action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
// installer app or null for registered apps. The callback only need to send back to the
// registered apps so we check the null condition here.
- notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
+ notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList,
+ null /* filterExtras */);
}
}
@@ -975,14 +976,16 @@
final Bundle options = new BroadcastOptions()
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
.toBundle();
+ BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver =
+ (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
+ snapshot, callingUid, intentExtras);
mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */,
extras, flags, null /* targetPkg */, null /* finishedReceiver */,
new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
- (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
- snapshot, callingUid, intentExtras),
+ filterExtrasForReceiver,
options));
notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver);
}
void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot,
@@ -1068,9 +1071,10 @@
@Nullable Bundle extras,
@NonNull int[] userIds,
@NonNull int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList) {
+ @Nullable SparseArray<int[]> broadcastAllowList,
+ @Nullable BiFunction<Integer, Bundle, Bundle> filterExtras) {
mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
- instantUserIds, broadcastAllowList, mHandler);
+ instantUserIds, broadcastAllowList, mHandler, filterExtras);
}
private void notifyResourcesChanged(boolean mediaStatus,
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b96b704..c920ca8 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -829,6 +829,9 @@
int returnCodeOfChild;
for (int childId : childUserIds) {
if (childId == userId) continue;
+ if (mUserManagerInternal.getProfileParentId(childId) != userId) {
+ continue;
+ }
// If package is not present in child then don't attempt to delete.
if (!packageState.getUserStateOrDefault(childId).isInstalled()) {
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 3b9f9c8..41d0176 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -46,11 +46,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.pm.parsing.PackageCacher;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.utils.WatchedArrayMap;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 65bfb2f..b638d30 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -146,6 +146,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -154,6 +155,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
@@ -178,7 +180,6 @@
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.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -672,6 +673,9 @@
if (pkgSetting == null || pkgSetting.getPkg() == null) {
return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
}
+ if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp())) {
+ return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
+ }
if (!snapshot.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
// only allow the existing package to be used if it's installed as a full
// application for at least one user
@@ -1167,8 +1171,9 @@
parseFlags);
archivedPackage = request.getPackageLite().getArchivedPackage();
}
- } catch (PackageManagerException | PackageParserException e) {
- throw new PrepareFailure("Failed parse during installPackageLI", e);
+ } catch (PackageParserException e) {
+ throw new PrepareFailure(e.error,
+ ExceptionUtils.getCompleteMessage("Failed parse during installPackageLI", e));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2858,14 +2863,17 @@
mPm.notifyPackageChanged(packageName, request.getAppId());
}
- for (int userId : firstUserIds) {
- // Apply restricted settings on potentially dangerous packages. Needs to happen
- // after appOpsManager is notified of the new package
- if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
- || request.getPackageSource()
- == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
- enableRestrictedSettings(packageName, request.getAppId(), userId);
- }
+ // Apply restricted settings on potentially dangerous packages. Needs to happen
+ // after appOpsManager is notified of the new package
+ if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+ || request.getPackageSource()
+ == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
+ final int appId = request.getAppId();
+ mPm.mHandler.post(() -> {
+ for (int userId : firstUserIds) {
+ enableRestrictedSettings(packageName, appId, userId);
+ }
+ });
}
// Log current value of "unknown sources" setting
@@ -3680,6 +3688,8 @@
final ParsedPackage parsedPackage;
try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
+ } catch (PackageParserException e) {
+ throw new PackageManagerException(e.error, e.getMessage(), e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index 187cada..e970d2c 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -51,8 +51,8 @@
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.util.Preconditions;
-import com.android.server.pm.parsing.PackageParser2;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 2864a8b..dcfc855d 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -132,6 +132,11 @@
private static final String EXTRA_INSTALLER_TITLE =
"com.android.content.pm.extra.UNARCHIVE_INSTALLER_TITLE";
+ private static final PorterDuffColorFilter OPACITY_LAYER_FILTER =
+ new PorterDuffColorFilter(
+ Color.argb(0.5f /* alpha */, 0f /* red */, 0f /* green */, 0f /* blue */),
+ PorterDuff.Mode.SRC_ATOP);
+
private final Context mContext;
private final PackageManagerService mPm;
@@ -746,11 +751,7 @@
return bitmap;
}
BitmapDrawable appIconDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
- PorterDuffColorFilter colorFilter =
- new PorterDuffColorFilter(
- Color.argb(0.32f /* alpha */, 0f /* red */, 0f /* green */, 0f /* blue */),
- PorterDuff.Mode.SRC_ATOP);
- appIconDrawable.setColorFilter(colorFilter);
+ appIconDrawable.setColorFilter(OPACITY_LAYER_FILTER);
appIconDrawable.setBounds(
0 /* left */,
0 /* top */,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2942bbb..cbd65a4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -111,6 +111,7 @@
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.util.ImageUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.TypedXmlPullParser;
@@ -120,7 +121,6 @@
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.utils.RequestThrottle;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1d5b8c3..5225529 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -181,6 +181,7 @@
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
@@ -221,8 +222,8 @@
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DynamicCodeLogger;
import com.android.server.pm.local.PackageManagerLocalImpl;
+import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerService;
@@ -606,6 +607,8 @@
private final boolean mIsUpgrade;
private final boolean mIsPreNMR1Upgrade;
private final boolean mIsPreQUpgrade;
+ // If mIsUpgrade == true, contains the prior SDK version, else -1.
+ private final int mPriorSdkVersion;
// Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
// LOCK HELD. Can be called with mInstallLock held.
@@ -1698,7 +1701,7 @@
() -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(),
- pm.mCacheDir,
+ new PackageCacher(pm.mCacheDir),
pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null,
pm.mPackageParserCallback) /* scanningPackageParserProducer */,
@@ -1890,6 +1893,7 @@
mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
mIsPreQUpgrade = testParams.isPreQupgrade;
+ mPriorSdkVersion = testParams.priorSdkVersion;
mIsUpgrade = testParams.isUpgrade;
mMetrics = testParams.Metrics;
mModuleInfoProvider = testParams.moduleInfoProvider;
@@ -2229,7 +2233,7 @@
"Upgrading from " + ver.fingerprint + " (" + ver.buildFingerprint + ") to "
+ PackagePartitions.FINGERPRINT + " (" + Build.FINGERPRINT + ")");
}
-
+ mPriorSdkVersion = mIsUpgrade ? ver.sdkVersion : -1;
mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
mInjector.getSystemPartitions());
@@ -4622,7 +4626,7 @@
});
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED,
packageName, extras, userIds, null /* instantUserIds */,
- broadcastAllowList, mHandler);
+ broadcastAllowList, mHandler, null /* filterExtras */);
}
}
}
@@ -7072,7 +7076,7 @@
}
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED,
packageName, extras, userIds, null /* instantUserIds */,
- broadcastAllowList, mHandler);
+ broadcastAllowList, mHandler, null /* filterExtras */);
}
@Override
@@ -7098,6 +7102,12 @@
mPackageMonitorCallbackHelper.notifyPackageMonitorWithIntent(intent, userId,
visibilityAllowList, mHandler);
}
+
+ @Override
+ public boolean isUpgradingFromLowerThan(int sdkVersion) {
+ final boolean isUpgrading = mPriorSdkVersion != -1;
+ return isUpgrading && mPriorSdkVersion < sdkVersion;
+ }
}
private void setEnabledOverlayPackages(@UserIdInt int userId,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index ebf1c04..049737d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -27,12 +27,12 @@
import android.util.DisplayMetrics;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.SystemConfig;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DynamicCodeLogger;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.resolution.ComponentResolver;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 655b9c9..2d79718 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -31,10 +31,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DynamicCodeLogger;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -65,6 +65,7 @@
public ComponentName instantAppResolverSettingsComponent;
public boolean isPreNmr1Upgrade;
public boolean isPreQupgrade;
+ public int priorSdkVersion = -1;
public boolean isUpgrade;
public LegacyPermissionManagerInternal legacyPermissionManagerInternal;
public DisplayMetrics Metrics;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 243fb16..5724ee0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1085,8 +1085,14 @@
// the sdk or package name along with optional additional information based on opt.
final Map<String, List<String>> out = new HashMap<>();
for (int userId : userIds) {
- final int translatedUserId =
+ final int translatedUserId;
+ try {
+ translatedUserId =
translateUserId(userId, UserHandle.USER_SYSTEM, "runListPackages");
+ } catch (RuntimeException ex) {
+ getErrPrintWriter().println("Error: " + ex.toString());
+ continue;
+ }
@SuppressWarnings("unchecked") final ParceledListSlice<PackageInfo> slice =
mInterface.getInstalledPackages(getFlags, translatedUserId);
final List<PackageInfo> packages = slice.getList();
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index 1bb0730..d05e4c6 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -41,6 +41,7 @@
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
+import java.util.function.BiFunction;
/** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly
* used by PackageMonitor to improve the broadcast latency. */
@@ -105,8 +106,9 @@
extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
- notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras ,
- userIds /* userIds */, instantUserIds, broadcastAllowList, handler);
+ notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras,
+ userIds /* userIds */, instantUserIds, broadcastAllowList, handler,
+ null /* filterExtras */);
}
public void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
@@ -120,7 +122,8 @@
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */, handler);
+ null /* instantUserIds */, null /* broadcastAllowList */, handler,
+ null /* filterExtras */);
}
public void notifyPackageChanged(String packageName, boolean dontKillApp,
@@ -137,12 +140,12 @@
extras.putString(Intent.EXTRA_REASON, reason);
}
notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds,
- instantUserIds, broadcastAllowList, handler);
+ instantUserIds, broadcastAllowList, handler, null /* filterExtras */);
}
public void notifyPackageMonitor(String action, String pkg, Bundle extras,
int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
- Handler handler) {
+ Handler handler, BiFunction<Integer, Bundle, Bundle> filterExtras) {
if (!isAllowedCallbackAction(action)) {
return;
}
@@ -160,10 +163,11 @@
if (ArrayUtils.isEmpty(instantUserIds)) {
doNotifyCallbacksByAction(
- action, pkg, extras, resolvedUserIds, broadcastAllowList, handler);
+ action, pkg, extras, resolvedUserIds, broadcastAllowList, handler,
+ filterExtras);
} else {
doNotifyCallbacksByAction(action, pkg, extras, instantUserIds, broadcastAllowList,
- handler);
+ handler, filterExtras);
}
} catch (RemoteException e) {
// do nothing
@@ -199,11 +203,13 @@
synchronized (mLock) {
callbacks = mCallbacks;
}
- doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler);
+ doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler,
+ null /* filterExtrasFunction */);
}
private void doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds,
- SparseArray<int[]> broadcastAllowList, Handler handler) {
+ SparseArray<int[]> broadcastAllowList, Handler handler,
+ BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
RemoteCallbackList<IRemoteCallback> callbacks;
synchronized (mLock) {
callbacks = mCallbacks;
@@ -223,12 +229,13 @@
final int[] allowUids =
broadcastAllowList != null ? broadcastAllowList.get(userId) : null;
- doNotifyCallbacks(callbacks, intent, userId, allowUids, handler);
+ doNotifyCallbacks(callbacks, intent, userId, allowUids, handler, filterExtrasFunction);
}
}
private void doNotifyCallbacks(RemoteCallbackList<IRemoteCallback> callbacks,
- Intent intent, int userId, int[] allowUids, Handler handler) {
+ Intent intent, int userId, int[] allowUids, Handler handler,
+ BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
handler.post(() -> callbacks.broadcast((callback, user) -> {
RegisterUser registerUser = (RegisterUser) user;
if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
@@ -236,6 +243,15 @@
return;
}
int registerUid = registerUser.getUid();
+ if (filterExtrasFunction != null) {
+ final Bundle extras = intent.getExtras();
+ if (extras != null) {
+ final Bundle filteredExtras = filterExtrasFunction.apply(registerUid, extras);
+ if (filteredExtras != null) {
+ intent.replaceExtras(filteredExtras);
+ }
+ }
+ }
if (allowUids != null && registerUid != Process.SYSTEM_UID
&& !ArrayUtils.contains(allowUids, registerUid)) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 15d2fdc..1fe49c7 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -41,10 +41,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.InstallLocationUtils;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.rollback.RollbackManagerInternal;
import java.io.File;
@@ -399,7 +400,7 @@
try (PackageParser2 packageParser = mPackageParserSupplier.get()) {
File apexFile = new File(apexInfo.modulePath);
parsedPackage = packageParser.parsePackage(apexFile, 0, false);
- } catch (PackageManagerException e) {
+ } catch (PackageParserException e) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
"Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 1089ac9..0511639 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -22,9 +22,10 @@
import android.os.Trace;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.ConcurrentUtils;
-import com.android.server.pm.parsing.PackageParser2;
import java.io.File;
import java.util.concurrent.ArrayBlockingQueue;
@@ -125,6 +126,10 @@
@VisibleForTesting
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageManagerException {
- return mPackageParser.parsePackage(scanFile, parseFlags, true);
+ try {
+ return mPackageParser.parsePackage(scanFile, parseFlags, true);
+ } catch (PackageParserException e) {
+ throw new PackageManagerException(e.error, e.getMessage(), e);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index edae273..b286b12 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -64,6 +64,7 @@
import android.os.Message;
import android.os.PatternMatcher;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.SELinux;
import android.os.SystemClock;
import android.os.Trace;
@@ -3189,6 +3190,9 @@
pkg.isScannedAsStoppedSystemApp());
if (!pkg.hasSharedUser()) {
serializer.attributeInt(null, "userId", pkg.getAppId());
+
+ serializer.attributeBoolean(null, "isSdkLibrary",
+ pkg.getAndroidPackage() != null && pkg.getAndroidPackage().isSdkLibrary());
} else {
serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
}
@@ -4039,10 +4043,12 @@
int targetSdkVersion = 0;
byte[] restrictUpdateHash = null;
boolean isScannedAsStoppedSystemApp = false;
+ boolean isSdkLibrary = false;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
appId = parseAppId(parser);
+ isSdkLibrary = parser.getAttributeBoolean(null, "isSdkLibrary", false);
sharedUserAppId = parseSharedUserAppId(parser);
codePathStr = parser.getAttributeValue(null, "codePath");
@@ -4157,7 +4163,8 @@
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <package> has no codePath at "
+ parser.getPositionDescription());
- } else if (appId > 0) {
+ } else if (appId > 0 || (appId == Process.INVALID_UID && isSdkLibrary
+ && Flags.disallowSdkLibsToBeApps())) {
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
appId, pkgFlags, pkgPrivateFlags, domainSetId);
if (PackageManagerService.DEBUG_SETTINGS)
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 79c9c8e..b6267c4 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -28,6 +28,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.IPackageCacher;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.ApexManager;
@@ -39,7 +40,7 @@
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
-public class PackageCacher {
+public class PackageCacher implements IPackageCacher {
private static final String TAG = "PackageCacher";
@@ -162,6 +163,7 @@
* Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
* or {@code null} if no cached result exists.
*/
+ @Override
public ParsedPackage getCachedResult(File packageFile, int flags) {
final String cacheKey = getCacheKey(packageFile, flags);
final File cacheFile = new File(mCacheDir, cacheKey);
@@ -192,6 +194,7 @@
/**
* Caches the parse result for {@code packageFile} with flags {@code flags}.
*/
+ @Override
public void cacheResult(File packageFile, int flags, ParsedPackage parsed) {
try {
final String cacheKey = getCacheKey(packageFile, flags);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParserUtils.java b/services/core/java/com/android/server/pm/parsing/PackageParserUtils.java
new file mode 100644
index 0000000..03a7a37
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/PackageParserUtils.java
@@ -0,0 +1,79 @@
+/*
+ * 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.pm.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.PackageManagerService;
+
+import java.util.Set;
+
+public class PackageParserUtils {
+ /**
+ * For parsing inside the system server but outside of {@link PackageManagerService}.
+ * Generally used for parsing information in an APK that hasn't been installed yet.
+ *
+ * This must be called inside the system process as it relies on {@link ServiceManager}.
+ */
+ @NonNull
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public static PackageParser2 forParsingFileWithDefaults() {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ return new PackageParser2(null /* separateProcesses */, null /* displayMetrics */,
+ null /* cacheDir */, new PackageParser2.Callback() {
+ @Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
+ try {
+ return platformCompat.isChangeEnabled(changeId, appInfo);
+ } catch (Exception e) {
+ // This shouldn't happen, but assume enforcement if it does
+ Slog.wtf(ParsingUtils.TAG, "IPlatformCompat query failed", e);
+ return true;
+ }
+ }
+
+ @Override
+ public boolean hasFeature(String feature) {
+ // Assume the device doesn't support anything. This will affect permission parsing
+ // and will force <uses-permission/> declarations to include all requiredNotFeature
+ // permissions and exclude all requiredFeature permissions. This mirrors the old
+ // behavior.
+ return false;
+ }
+
+ @Override
+ public Set<String> getHiddenApiWhitelistedApps() {
+ return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
+ }
+
+ @Override
+ public Set<String> getInstallConstraintsAllowlist() {
+ return SystemConfig.getInstance().getInstallConstraintsAllowlist();
+ }
+ });
+ }
+}
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 d3931a3..10e6edc 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,6 +26,7 @@
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;
@@ -1228,6 +1229,11 @@
sPlatformPermissions.put(permission, permissionInfo);
}
} catch (PackageManager.NameNotFoundException ignored) {
+ // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+ if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
+ Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as package"
+ + " not found when retrieving permission info");
+ }
return PermissionChecker.PERMISSION_HARD_DENIED;
}
}
@@ -1347,17 +1353,34 @@
// way we can avoid the datasource creating an attribution context for every call.
if (!(fromDatasource && current.equals(attributionSource))
&& next != null && !current.isTrusted(context)) {
+ // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+ if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
+ Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as "
+ + current + " attribution source isn't a data source and "
+ + current + " isn't trusted");
+ }
return PermissionChecker.PERMISSION_HARD_DENIED;
}
// If we already checked the permission for this one, skip the work
if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt,
permission, current)) {
+ // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+ if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
+ Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as we"
+ + " aren't skipping permission checks and permission check returns"
+ + " false for " + current);
+ }
return PermissionChecker.PERMISSION_HARD_DENIED;
}
if (next != null && !checkPermission(context, permissionManagerServiceInt,
permission, next)) {
+ // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+ if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
+ Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
+ + " permission check returns false for next source " + next);
+ }
return PermissionChecker.PERMISSION_HARD_DENIED;
}
@@ -1402,6 +1425,10 @@
switch (opMode) {
case AppOpsManager.MODE_ERRORED: {
+ if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
+ Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as op"
+ + " mode is MODE_ERRORED for " + attributionSource);
+ }
return PermissionChecker.PERMISSION_HARD_DENIED;
}
case AppOpsManager.MODE_IGNORED: {
@@ -1670,6 +1697,12 @@
final AttributionSource resolvedAttributionSource = resolveAttributionSource(
context, accessorSource);
if (resolvedAttributionSource.getPackageName() == null) {
+ // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+ if (op == OP_BLUETOOTH_CONNECT) {
+ Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as resolved"
+ + "package name for " + resolvedAttributionSource + " returned"
+ + " null");
+ }
return AppOpsManager.MODE_ERRORED;
}
int notedOp = op;
@@ -1683,6 +1716,13 @@
if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) {
checkedOpResult = appOpsManager.checkOpNoThrow(op, resolvedAttributionSource);
if (checkedOpResult == MODE_ERRORED) {
+ // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
+ if (op == OP_BLUETOOTH_CONNECT) {
+ Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
+ + " checkOp for resolvedAttributionSource "
+ + resolvedAttributionSource + " and op " + op
+ + " returned MODE_ERRORED");
+ }
return checkedOpResult;
}
notedOp = attributedOp;
@@ -1722,7 +1762,22 @@
throw new SecurityException(msg + ":" + e.getMessage());
}
}
- return Math.max(checkedOpResult, notedOpResult);
+ 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;
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 671e031..3afba39 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3291,7 +3291,7 @@
final PermissionAllowlist permissionAllowlist =
SystemConfig.getInstance().getPermissionAllowlist();
final String packageName = packageState.getPackageName();
- if (packageState.isVendor()) {
+ if (packageState.isVendor() || packageState.isOdm()) {
return permissionAllowlist.getVendorPrivilegedAppAllowlistState(packageName,
permissionName);
} else if (packageState.isProduct()) {
@@ -3386,7 +3386,7 @@
// the permission's protectionLevel does not have the extra 'vendorPrivileged'
// flag.
if (allowed && isPrivilegedPermission && !bp.isVendorPrivileged()
- && pkgSetting.isVendor()) {
+ && (pkgSetting.isVendor() || pkgSetting.isOdm())) {
Slog.w(TAG, "Permission " + permissionName
+ " cannot be granted to privileged vendor apk " + pkg.getPackageName()
+ " because it isn't a 'vendorPrivileged' permission.");
diff --git a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
index b531b0e..611e4ed 100644
--- a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
+++ b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java
@@ -76,6 +76,18 @@
getActionsBufferWithLazyCleanUp(keyCode, downTime).setExecutable();
}
+ /**
+ * Clears all the queued action for given key code.
+ *
+ * @param keyCode the key code whose queued actions will be cleared.
+ */
+ public void cancelQueuedAction(int keyCode) {
+ TimedActionsBuffer actionsBuffer = mBuffers.get(keyCode);
+ if (actionsBuffer != null) {
+ actionsBuffer.clear();
+ }
+ }
+
private TimedActionsBuffer getActionsBufferWithLazyCleanUp(int keyCode, long downTime) {
TimedActionsBuffer buffer = mBuffers.get(keyCode);
if (buffer == null || buffer.getDownTime() != downTime) {
@@ -146,6 +158,10 @@
mActions.clear();
}
+ void clear() {
+ mActions.clear();
+ }
+
void dump(String prefix, PrintWriter pw) {
if (mExecutable) {
pw.println(prefix + " " + KeyEvent.keyCodeToString(mKeyCode) + ": executable");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 938ed23..edce3ec 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -103,6 +103,7 @@
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IUiModeManager;
@@ -472,6 +473,8 @@
private TalkbackShortcutController mTalkbackShortcutController;
+ private WindowWakeUpPolicy mWindowWakeUpPolicy;
+
boolean mSafeMode;
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
@@ -584,6 +587,10 @@
private int mLongPressOnStemPrimaryBehavior;
private RecentTaskInfo mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;
+ // The focused task at the time when the first STEM_PRIMARY key was released. This can only
+ // be accessed from the looper thread.
+ private RootTaskInfo mFocusedTaskInfoOnStemPrimarySingleKeyUp;
+
private boolean mHandleVolumeKeysInWM;
private boolean mPendingKeyguardOccluded;
@@ -635,15 +642,6 @@
// Whether to lock the device after the next dreaming transition has finished.
private boolean mLockAfterDreamingTransitionFinished;
- // Allowed theater mode wake actions
- private boolean mAllowTheaterModeWakeFromKey;
- private boolean mAllowTheaterModeWakeFromPowerKey;
- private boolean mAllowTheaterModeWakeFromMotion;
- private boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming;
- private boolean mAllowTheaterModeWakeFromCameraLens;
- private boolean mAllowTheaterModeWakeFromLidSwitch;
- private boolean mAllowTheaterModeWakeFromWakeGesture;
-
// If true, the power button long press behavior will be invoked even if the default display is
// non-interactive. If false, the power button long press behavior will be skipped if the
// default display is non-interactive.
@@ -925,8 +923,7 @@
if (shouldEnableWakeGestureLp()) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Wake Up");
- wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
- PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");
+ mWindowWakeUpPolicy.wakeUpFromWakeGesture();
}
}
}
@@ -1062,7 +1059,7 @@
|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
if (!mPowerKeyHandled) {
if (!interactive) {
- wakeUpFromPowerKey(event.getDownTime());
+ wakeUpFromWakeKey(event);
}
} else {
// handled by another power key policy.
@@ -1304,7 +1301,7 @@
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0);
if (!interactive) {
- wakeUpFromPowerKey(eventTime);
+ wakeUpFromWakeKey(eventTime, KEYCODE_POWER);
}
} else {
Slog.i(TAG, "Toggling theater mode on.");
@@ -1320,7 +1317,7 @@
case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
Slog.i(TAG, "Starting brightness boost.");
if (!interactive) {
- wakeUpFromPowerKey(eventTime);
+ wakeUpFromWakeKey(eventTime, KEYCODE_POWER);
}
mPowerManager.boostScreenBrightness(eventTime);
break;
@@ -2135,12 +2132,10 @@
static class Injector {
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
- private final Looper mLooper;
- Injector(Context context, WindowManagerFuncs funcs, Looper looper) {
+ Injector(Context context, WindowManagerFuncs funcs) {
mContext = context;
mWindowManagerFuncs = funcs;
- mLooper = looper;
}
Context getContext() {
@@ -2152,7 +2147,7 @@
}
Looper getLooper() {
- return mLooper;
+ return Looper.myLooper();
}
AccessibilityShortcutController getAccessibilityShortcutController(
@@ -2195,7 +2190,7 @@
/** {@inheritDoc} */
@Override
public void init(Context context, WindowManagerFuncs funcs) {
- init(new Injector(context, funcs, Looper.myLooper()));
+ init(new Injector(context, funcs));
}
@VisibleForTesting
@@ -2309,22 +2304,6 @@
mLidNavigationAccessibility = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lidNavigationAccessibility);
- mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
- mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
- || mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
- mAllowTheaterModeWakeFromMotion = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
- mAllowTheaterModeWakeFromMotionWhenNotDreaming = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming);
- mAllowTheaterModeWakeFromCameraLens = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
- mAllowTheaterModeWakeFromLidSwitch = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
- mAllowTheaterModeWakeFromWakeGesture = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
-
mGoToSleepOnButtonPressTheaterMode = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_goToSleepOnButtonPressTheaterMode);
@@ -2454,6 +2433,7 @@
com.android.internal.R.integer.config_keyguardDrawnTimeout);
mKeyguardDelegate = injector.getKeyguardServiceDelegate();
mTalkbackShortcutController = injector.getTalkbackShortcutController();
+ mWindowWakeUpPolicy = new WindowWakeUpPolicy(mContext);
initKeyCombinationRules();
initSingleKeyGestureRules(injector.getLooper());
mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
@@ -2723,7 +2703,7 @@
@Override
void onPress(long downTime, int unusedDisplayId) {
- if (mShouldEarlyShortPressOnStemPrimary) {
+ if (shouldHandleStemPrimaryEarlyShortPress()) {
return;
}
// Short-press should be triggered only if app doesn't handle it.
@@ -2747,6 +2727,11 @@
if (count == 3
&& mTriplePressOnStemPrimaryBehavior
== TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY) {
+ // Cancel any queued actions for current key code to prevent them from being
+ // launched after a11y layer enabled. If the action happens early,
+ // undoEarlySinglePress will make sure the correct task is on top.
+ mDeferredKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY);
+ undoEarlySinglePress();
stemPrimaryPress(count);
} else {
// Other multi-press gestures should be triggered only if app doesn't handle it.
@@ -2755,6 +2740,27 @@
}
}
+ /**
+ * This method undo the previously launched early-single-press action by bringing the
+ * focused task before launching early-single-press back to top.
+ */
+ private void undoEarlySinglePress() {
+ if (shouldHandleStemPrimaryEarlyShortPress()
+ && mFocusedTaskInfoOnStemPrimarySingleKeyUp != null) {
+ try {
+ mActivityManagerService.startActivityFromRecents(
+ mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId, null);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Slog.e(
+ TAG,
+ "Failed to start task "
+ + mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId
+ + " from recents",
+ e);
+ }
+ }
+ }
+
@Override
void onKeyUp(long eventTime, int count, int unusedDisplayId) {
if (count == 1) {
@@ -2763,15 +2769,49 @@
// It is possible that we may navigate away from this task before the double
// press is detected, as a result of the first press, so we save the current
// most recent task before that happens.
+ // TODO(b/311497918): guard this with DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP
mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp =
mActivityTaskManagerInternal.getMostRecentTaskFromBackground();
- if (mShouldEarlyShortPressOnStemPrimary) {
+
+ mFocusedTaskInfoOnStemPrimarySingleKeyUp = null;
+
+ if (shouldHandleStemPrimaryEarlyShortPress()) {
// Key-up gesture should be triggered only if app doesn't handle it.
mDeferredKeyActionExecutor.queueKeyAction(
- KeyEvent.KEYCODE_STEM_PRIMARY, eventTime, () -> stemPrimaryPress(1));
+ KeyEvent.KEYCODE_STEM_PRIMARY,
+ eventTime,
+ () -> {
+ // Save the info of the focused task on screen. This may be used
+ // later to bring the current focused task back to top. For
+ // example, stem primary triple press enables the A11y interface
+ // on top of the current focused task. When early single press is
+ // enabled for stem primary, the focused task could change to
+ // something else upon first key up event. In that case, we will
+ // bring the task recorded by this variable back to top. Then, start
+ // A11y interface.
+ try {
+ mFocusedTaskInfoOnStemPrimarySingleKeyUp =
+ mActivityManagerService.getFocusedRootTaskInfo();
+ } catch (RemoteException e) {
+ Slog.e(
+ TAG,
+ "StemPrimaryKeyRule: onKeyUp: error while getting "
+ + "focused task "
+ + "info.",
+ e);
+ }
+
+ stemPrimaryPress(1);
+ });
}
}
}
+
+ // TODO(b/311497918): make a shouldHandlePowerEarlyShortPress for power button.
+ private boolean shouldHandleStemPrimaryEarlyShortPress() {
+ return mShouldEarlyShortPressOnStemPrimary
+ && mShortPressOnStemPrimaryBehavior == SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
+ }
}
private void initSingleKeyGestureRules(Looper looper) {
@@ -4420,8 +4460,7 @@
updateRotation(true);
if (lidOpen) {
- wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch,
- PowerManager.WAKE_REASON_LID, "android.policy:LID");
+ mWindowWakeUpPolicy.wakeUpFromLid();
} else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
@@ -4447,8 +4486,7 @@
} else {
intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
}
- wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens,
- PowerManager.WAKE_REASON_CAMERA_LAUNCH, "android.policy:CAMERA_COVER");
+ mWindowWakeUpPolicy.wakeUpFromCameraCover(whenNanos / 1000000);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
mCameraLensCoverState = lensCoverState;
@@ -4526,7 +4564,7 @@
boolean shouldTurnOnTv = false;
if (down && (keyCode == KeyEvent.KEYCODE_POWER
|| keyCode == KeyEvent.KEYCODE_TV_POWER)) {
- wakeUpFromPowerKey(event.getDownTime());
+ wakeUpFromWakeKey(event);
shouldTurnOnTv = true;
} else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
&& isWakeKeyWhenScreenOff(keyCode)) {
@@ -5041,9 +5079,7 @@
if (mRequestedOrSleepingDefaultDisplay) {
mCameraGestureTriggeredDuringGoingToSleep = true;
// Wake device up early to prevent display doing redundant turning off/on stuff.
- wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey,
- PowerManager.WAKE_REASON_CAMERA_LAUNCH,
- "android.policy:CAMERA_GESTURE_PREVENT_LOCK");
+ mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture();
}
return true;
}
@@ -5138,11 +5174,10 @@
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
- public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
- int policyFlags) {
+ public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+ long whenNanos, int policyFlags) {
if ((policyFlags & FLAG_WAKE) != 0) {
- if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
- PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
+ if (mWindowWakeUpPolicy.wakeUpFromMotion(whenNanos / 1000000)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
@@ -5156,8 +5191,7 @@
// there will be no dream to intercept the touch and wake into ambient. The device should
// wake up in this case.
if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
- if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
- PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
+ if (mWindowWakeUpPolicy.wakeUpFromMotion(whenNanos / 1000000)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
@@ -5491,37 +5525,22 @@
return sleepDurationRealtime > mWakeUpToLastStateTimeout;
}
- private void wakeUpFromPowerKey(long eventTime) {
- if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
- PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {
- // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
- if (shouldWakeUpWithHomeIntent()) {
- startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,
- PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));
- }
- }
- }
-
private void wakeUpFromWakeKey(KeyEvent event) {
- if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey,
- PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) {
- // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
- if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) {
- startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true,
- PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_WAKE_KEY));
- }
- }
+ wakeUpFromWakeKey(event.getEventTime(), event.getKeyCode());
}
- private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
- String details) {
- final boolean theaterModeEnabled = isTheaterModeEnabled();
- if (!wakeInTheaterMode && theaterModeEnabled) {
- return false;
+ private void wakeUpFromWakeKey(long eventTime, int keyCode) {
+ if (mWindowWakeUpPolicy.wakeUpFromKey(eventTime, keyCode)) {
+ final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER;
+ // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
+ if (shouldWakeUpWithHomeIntent() && keyCanLaunchHome) {
+ startDockOrHome(
+ DEFAULT_DISPLAY,
+ /*fromHomeKey*/ keyCode == KEYCODE_HOME,
+ /*wakenFromDreams*/ true,
+ "Wake from " + KeyEvent. keyCodeToString(keyCode));
+ }
}
-
- mPowerManager.wakeUp(wakeTime, reason, details);
- return true;
}
private void finishKeyguardDrawn() {
@@ -6865,13 +6884,13 @@
static class ButtonOverridePermissionChecker {
boolean canAppOverrideSystemKey(Context context, int uid) {
return PermissionChecker.checkPermissionForDataDelivery(
- context,
- OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW,
- PID_UNKNOWN,
- uid,
- null,
- null,
- null)
+ context,
+ OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW,
+ PID_UNKNOWN,
+ uid,
+ null,
+ null,
+ null)
== PERMISSION_GRANTED;
}
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 03a7bd3..3016b39 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -706,12 +706,14 @@
* Generally, it's best to keep as little as possible in the queue thread
* because it's the most fragile.
* @param displayId The display ID of the motion event.
+ * @param source the {@link InputDevice} source that caused the motion.
+ * @param action the {@link MotionEvent} action for the motion.
* @param policyFlags The policy flags associated with the motion.
*
* @return Actions flags: may be {@link #ACTION_PASS_TO_USER}.
*/
- int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
- int policyFlags);
+ int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+ long whenNanos, int policyFlags);
/**
* Called from the input dispatcher thread before a key is dispatched to a window.
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
new file mode 100644
index 0000000..392d0d4
--- /dev/null
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -0,0 +1,176 @@
+/*
+ * 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.policy;
+
+import static android.os.PowerManager.WAKE_REASON_CAMERA_LAUNCH;
+import static android.os.PowerManager.WAKE_REASON_GESTURE;
+import static android.os.PowerManager.WAKE_REASON_LID;
+import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
+import static android.os.PowerManager.WAKE_REASON_WAKE_KEY;
+import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
+import static android.view.KeyEvent.KEYCODE_POWER;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeReason;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Slog;
+
+
+/** Policy controlling the decision and execution of window-related wake ups. */
+class WindowWakeUpPolicy {
+ private static final String TAG = "WindowWakeUpPolicy";
+
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+ private final PowerManager mPowerManager;
+
+ private final boolean mAllowTheaterModeWakeFromKey;
+ private final boolean mAllowTheaterModeWakeFromPowerKey;
+ private final boolean mAllowTheaterModeWakeFromMotion;
+ private final boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming;
+ private final boolean mAllowTheaterModeWakeFromCameraLens;
+ private final boolean mAllowTheaterModeWakeFromLidSwitch;
+ private final boolean mAllowTheaterModeWakeFromWakeGesture;
+
+ WindowWakeUpPolicy(Context context) {
+ mContext = context;
+ mPowerManager = context.getSystemService(PowerManager.class);
+
+ final Resources res = context.getResources();
+ mAllowTheaterModeWakeFromKey = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
+ mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
+ || res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
+ mAllowTheaterModeWakeFromMotion = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
+ mAllowTheaterModeWakeFromMotionWhenNotDreaming = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming);
+ mAllowTheaterModeWakeFromCameraLens = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
+ mAllowTheaterModeWakeFromLidSwitch = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
+ mAllowTheaterModeWakeFromWakeGesture = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
+ }
+
+ /**
+ * Wakes up from a key event.
+ *
+ * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+ * @param keyCode the {@link android.view.KeyEvent} key code of the key event.
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromKey(long eventTime, int keyCode) {
+ final boolean wakeAllowedDuringTheaterMode =
+ keyCode == KEYCODE_POWER
+ ? mAllowTheaterModeWakeFromPowerKey
+ : mAllowTheaterModeWakeFromKey;
+ return wakeUp(
+ eventTime,
+ wakeAllowedDuringTheaterMode,
+ keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
+ keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+ }
+
+ /**
+ * Wakes up from a motion event.
+ *
+ * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromMotion(long eventTime) {
+ return wakeUp(
+ eventTime, mAllowTheaterModeWakeFromMotion, WAKE_REASON_WAKE_MOTION, "MOTION");
+ }
+
+ /**
+ * Wakes up due to an opened camera cover.
+ *
+ * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromCameraCover(long eventTime) {
+ return wakeUp(
+ eventTime,
+ mAllowTheaterModeWakeFromCameraLens,
+ WAKE_REASON_CAMERA_LAUNCH,
+ "CAMERA_COVER");
+ }
+
+ /**
+ * Wakes up due to an opened lid.
+ *
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromLid() {
+ return wakeUp(
+ SystemClock.uptimeMillis(),
+ mAllowTheaterModeWakeFromLidSwitch,
+ WAKE_REASON_LID,
+ "LID");
+ }
+
+ /**
+ * Wakes up to prevent sleeping when opening camera through power button.
+ *
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromPowerKeyCameraGesture() {
+ return wakeUp(
+ SystemClock.uptimeMillis(),
+ mAllowTheaterModeWakeFromPowerKey,
+ WAKE_REASON_CAMERA_LAUNCH,
+ "CAMERA_GESTURE_PREVENT_LOCK");
+ }
+
+ /**
+ * Wake up from a wake gesture.
+ *
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromWakeGesture() {
+ return wakeUp(
+ SystemClock.uptimeMillis(),
+ mAllowTheaterModeWakeFromWakeGesture,
+ WAKE_REASON_GESTURE,
+ "GESTURE");
+ }
+
+ private boolean wakeUp(
+ long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, String details) {
+ final boolean isTheaterModeEnabled =
+ Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1;
+ if (!wakeInTheaterMode && isTheaterModeEnabled) {
+ if (DEBUG) Slog.d(TAG, "Unable to wake up from " + details);
+ return false;
+ }
+ mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 7c833cb..6f75439 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -481,6 +481,11 @@
protected long mTargetDurationNanos;
protected boolean mUpdateAllowed;
protected int[] mNewThreadIds;
+ protected boolean mPowerEfficient;
+
+ private enum SessionModes {
+ POWER_EFFICIENCY,
+ };
protected AppHintSession(
int uid, int pid, int[] threadIds, IBinder token,
@@ -492,6 +497,7 @@
mHalSessionPtr = halSessionPtr;
mTargetDurationNanos = durationNanos;
mUpdateAllowed = true;
+ mPowerEfficient = false;
final boolean allowed = mUidObserver.isUidForeground(mUid);
updateHintAllowed(allowed);
try {
@@ -634,6 +640,9 @@
}
Preconditions.checkArgument(mode >= 0, "the mode Id value should be"
+ " greater than zero.");
+ if (mode == SessionModes.POWER_EFFICIENCY.ordinal()) {
+ mPowerEfficient = enabled;
+ }
mNativeWrapper.halSetMode(mHalSessionPtr, mode, enabled);
}
}
@@ -653,6 +662,12 @@
}
}
+ public boolean isPowerEfficient() {
+ synchronized (this) {
+ return mPowerEfficient;
+ }
+ }
+
void validateWorkDuration(WorkDuration workDuration) {
if (DEBUG) {
Slogf.d(TAG, "WorkDuration(" + workDuration.getTimestampNanos() + ", "
@@ -718,6 +733,7 @@
pw.println(prefix + "SessionTIDs: " + Arrays.toString(mThreadIds));
pw.println(prefix + "SessionTargetDurationNanos: " + mTargetDurationNanos);
pw.println(prefix + "SessionAllowed: " + mUpdateAllowed);
+ pw.println(prefix + "PowerEfficient: " + (mPowerEfficient ? "true" : "false"));
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 59b55bf7..0a7872f 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -17,11 +17,13 @@
package com.android.server.vibrator;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.os.IExternalVibratorService;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.Slog;
@@ -56,6 +58,8 @@
private final VibrationSettings mSettingsController;
private final int mDefaultVibrationAmplitude;
+ private SparseArray<Float> mAdaptiveHapticsScales;
+
VibrationScaler(Context context, VibrationSettings settingsController) {
mSettingsController = settingsController;
mDefaultVibrationAmplitude = context.getResources().getInteger(
@@ -140,6 +144,15 @@
if (scaleLevel != null) {
segment = segment.scale(scaleLevel.factor);
}
+
+ // If adaptive haptics scaling is available for this usage, apply it to the segment.
+ if (Flags.adaptiveHapticsEnabled()
+ && mAdaptiveHapticsScales != null && mAdaptiveHapticsScales.size() > 0
+ && mAdaptiveHapticsScales.contains(usageHint)) {
+ float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
+ segment = segment.scale(adaptiveScale);
+ }
+
segments.set(i, segment);
}
if (segments.equals(composedEffect.getSegments())) {
@@ -173,6 +186,16 @@
return prebaked.applyEffectStrength(newEffectStrength);
}
+ /**
+ * Updates the adaptive haptics scales.
+ * @param scales the new vibration scales to apply.
+ *
+ * @hide
+ */
+ public void updateAdaptiveHapticsScales(@Nullable SparseArray<Float> scales) {
+ mAdaptiveHapticsScales = scales;
+ }
+
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
private static int intensityToEffectStrength(int intensity) {
switch (intensity) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index 2eeb903..9d75249 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -16,14 +16,26 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_MEDIA;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.frameworks.vibrator.IVibratorControlService;
import android.frameworks.vibrator.IVibratorController;
+import android.frameworks.vibrator.ScaleParam;
import android.frameworks.vibrator.VibrationParam;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
import java.util.Objects;
@@ -37,10 +49,13 @@
private static final String TAG = "VibratorControlService";
private final VibratorControllerHolder mVibratorControllerHolder;
+ private final VibrationScaler mVibrationScaler;
private final Object mLock;
- public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) {
+ public VibratorControlService(VibratorControllerHolder vibratorControllerHolder,
+ VibrationScaler vibrationScaler, Object lock) {
mVibratorControllerHolder = vibratorControllerHolder;
+ mVibrationScaler = vibrationScaler;
mLock = lock;
}
@@ -70,25 +85,62 @@
+ "controller doesn't match the registered one. " + this);
return;
}
+ updateAdaptiveHapticsScales(/* params= */ null);
mVibratorControllerHolder.setVibratorController(null);
}
}
@Override
- public void setVibrationParams(
- @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token)
- throws RemoteException {
- // TODO(b/305939964): Add set vibration implementation.
+ public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params,
+ @NonNull IVibratorController token) throws RemoteException {
+ Objects.requireNonNull(token);
+
+ synchronized (mLock) {
+ if (mVibratorControllerHolder.getVibratorController() == null) {
+ Slog.w(TAG, "Received request to set VibrationParams for IVibratorController = "
+ + token + ", but no controller was previously registered. Request "
+ + "Ignored.");
+ return;
+ }
+ if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+ token.asBinder())) {
+ Slog.wtf(TAG, "Failed to set new VibrationParams. The provided "
+ + "controller doesn't match the registered one. " + this);
+ return;
+ }
+
+ updateAdaptiveHapticsScales(params);
+ }
}
@Override
- public void clearVibrationParams(int types, IVibratorController token) throws RemoteException {
- // TODO(b/305939964): Add clear vibration implementation.
+ public void clearVibrationParams(int types, @NonNull IVibratorController token)
+ throws RemoteException {
+ Objects.requireNonNull(token);
+
+ synchronized (mLock) {
+ if (mVibratorControllerHolder.getVibratorController() == null) {
+ Slog.w(TAG, "Received request to clear VibrationParams for IVibratorController = "
+ + token + ", but no controller was previously registered. Request "
+ + "Ignored.");
+ return;
+ }
+ if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(),
+ token.asBinder())) {
+ Slog.wtf(TAG, "Failed to clear VibrationParams. The provided "
+ + "controller doesn't match the registered one. " + this);
+ return;
+ }
+ //TODO(305942827): Update this method to only clear the specified vibration types.
+ // Perhaps look into whether it makes more sense to have this clear all scales and
+ // rely on setVibrationParams for clearing the scales for specific vibrations.
+ updateAdaptiveHapticsScales(/* params= */ null);
+ }
}
@Override
public void onRequestVibrationParamsComplete(
- IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
+ @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result)
throws RemoteException {
// TODO(305942827): Cache the vibration params in VibrationScaler
}
@@ -102,4 +154,52 @@
public String getInterfaceHash() throws RemoteException {
return this.HASH;
}
+
+ /**
+ * Extracts the vibration scales and caches them in {@link VibrationScaler}.
+ *
+ * @param params the new vibration params to cache.
+ */
+ private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) {
+ if (params == null || params.length == 0) {
+ mVibrationScaler.updateAdaptiveHapticsScales(null);
+ return;
+ }
+
+ SparseArray<Float> vibrationScales = new SparseArray<>();
+ for (int i = 0; i < params.length; i++) {
+ ScaleParam scaleParam = params[i].getScale();
+ extractVibrationScales(scaleParam, vibrationScales);
+ }
+ mVibrationScaler.updateAdaptiveHapticsScales(vibrationScales);
+ }
+
+ /**
+ * Extracts the vibration scales and map them to their corresponding
+ * {@link android.os.VibrationAttributes} usages.
+ */
+ private void extractVibrationScales(ScaleParam scaleParam, SparseArray<Float> vibrationScales) {
+ if ((ScaleParam.TYPE_ALARM & scaleParam.typesMask) != 0) {
+ vibrationScales.put(USAGE_ALARM, scaleParam.scale);
+ }
+
+ if ((ScaleParam.TYPE_NOTIFICATION & scaleParam.typesMask) != 0) {
+ vibrationScales.put(USAGE_NOTIFICATION, scaleParam.scale);
+ vibrationScales.put(USAGE_COMMUNICATION_REQUEST, scaleParam.scale);
+ }
+
+ if ((ScaleParam.TYPE_RINGTONE & scaleParam.typesMask) != 0) {
+ vibrationScales.put(USAGE_RINGTONE, scaleParam.scale);
+ }
+
+ if ((ScaleParam.TYPE_MEDIA & scaleParam.typesMask) != 0) {
+ vibrationScales.put(USAGE_MEDIA, scaleParam.scale);
+ vibrationScales.put(USAGE_UNKNOWN, scaleParam.scale);
+ }
+
+ if ((ScaleParam.TYPE_INTERACTIVE & scaleParam.typesMask) != 0) {
+ vibrationScales.put(USAGE_TOUCH, scaleParam.scale);
+ vibrationScales.put(USAGE_HARDWARE_FEEDBACK, scaleParam.scale);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index fc824ab..2c1ab95 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -273,7 +273,8 @@
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
injector.addService(VIBRATOR_CONTROL_SERVICE,
- new VibratorControlService(new VibratorControllerHolder(), mLock));
+ new VibratorControlService(new VibratorControllerHolder(), mVibrationScaler,
+ mLock));
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1577cef..77b4a74 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -448,16 +448,16 @@
}
}
- void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
+ void drawMagnifiedRegionBorderIfNeeded(int displayId) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(
TAG + ".drawMagnifiedRegionBorderIfNeeded",
FLAGS_MAGNIFICATION_CALLBACK,
- "displayId=" + displayId + "; transaction={" + t + "}");
+ "displayId=" + displayId);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t);
+ displayMagnifier.drawMagnifiedRegionBorderIfNeeded();
}
// Not relevant for the window observer.
}
@@ -855,12 +855,12 @@
.sendToTarget();
}
- void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
+ void drawMagnifiedRegionBorderIfNeeded() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
- FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}");
+ FLAGS_MAGNIFICATION_CALLBACK);
}
- mMagnifedViewport.drawWindowIfNeeded(t);
+ mMagnifedViewport.drawWindowIfNeeded();
}
void dump(PrintWriter pw, String prefix) {
@@ -1106,11 +1106,11 @@
}
void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
- if (shown) {
+ if (mWindow.setShown(shown, animate)) {
mFullRedrawNeeded = true;
+ // Clear the old region, so recomputeBounds will refresh the current region.
mOldMagnificationRegion.set(0, 0, 0, 0);
}
- mWindow.setShown(shown, animate);
}
void getMagnifiedFrameInContentCoords(Rect rect) {
@@ -1128,9 +1128,9 @@
return mMagnificationSpec;
}
- void drawWindowIfNeeded(SurfaceControl.Transaction t) {
+ void drawWindowIfNeeded() {
recomputeBounds();
- mWindow.drawIfNeeded(t);
+ mWindow.postDrawIfNeeded();
}
void destroyWindow() {
@@ -1158,7 +1158,7 @@
mWindow.dump(pw, prefix);
}
- private final class ViewportWindow {
+ private final class ViewportWindow implements Runnable {
private static final String SURFACE_TITLE = "Magnification Overlay";
private final Region mBounds = new Region();
@@ -1166,15 +1166,18 @@
private final Paint mPaint = new Paint();
private final SurfaceControl mSurfaceControl;
+ /** After initialization, it should only be accessed from animation thread. */
+ private final SurfaceControl.Transaction mTransaction;
private final BLASTBufferQueue mBlastBufferQueue;
private final Surface mSurface;
private final AnimationController mAnimationController;
private boolean mShown;
+ private boolean mLastSurfaceShown;
private int mAlpha;
- private boolean mInvalidated;
+ private volatile boolean mInvalidated;
ViewportWindow(Context context) {
SurfaceControl surfaceControl = null;
@@ -1202,6 +1205,7 @@
InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
mDisplayContent.getDisplayId(), "Magnification Overlay");
t.apply();
+ mTransaction = t;
mSurface = mBlastBufferQueue.createSurface();
mAnimationController = new AnimationController(context,
@@ -1219,10 +1223,11 @@
mInvalidated = true;
}
- void setShown(boolean shown, boolean animate) {
+ /** Returns {@code true} if the shown state is changed. */
+ boolean setShown(boolean shown, boolean animate) {
synchronized (mService.mGlobalLock) {
if (mShown == shown) {
- return;
+ return false;
}
mShown = shown;
mAnimationController.onFrameShownStateChanged(shown, animate);
@@ -1230,6 +1235,7 @@
Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
}
}
+ return true;
}
@SuppressWarnings("unused")
@@ -1285,7 +1291,22 @@
mService.scheduleAnimationLocked();
}
- void drawIfNeeded(SurfaceControl.Transaction t) {
+ void postDrawIfNeeded() {
+ if (mInvalidated) {
+ mService.mAnimationHandler.post(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ drawIfNeeded();
+ }
+
+ /**
+ * This method must only be called by animation handler directly to make sure
+ * thread safe and there is no lock held outside.
+ */
+ private void drawIfNeeded() {
// Drawing variables (alpha, dirty rect, and bounds) access is synchronized
// using WindowManagerGlobalLock. Grab copies of these values before
// drawing on the canvas so that drawing can be performed outside of the lock.
@@ -1314,6 +1335,7 @@
}
}
+ final boolean showSurface;
// Draw without holding WindowManagerGlobalLock.
if (alpha > 0) {
Canvas canvas = null;
@@ -1329,9 +1351,17 @@
mPaint.setAlpha(alpha);
canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
mSurface.unlockCanvasAndPost(canvas);
- t.show(mSurfaceControl);
+ showSurface = true;
} else {
- t.hide(mSurfaceControl);
+ showSurface = false;
+ }
+
+ if (showSurface && !mLastSurfaceShown) {
+ mTransaction.show(mSurfaceControl).apply();
+ mLastSurfaceShown = true;
+ } else if (!showSurface && mLastSurfaceShown) {
+ mTransaction.hide(mSurfaceControl).apply();
+ mLastSurfaceShown = false;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f3922f9..a43e7d5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -125,6 +125,7 @@
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
+import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -222,7 +223,6 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -495,10 +495,7 @@
private final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
- private CharSequence nonLocalizedLabel; // the label information from the package mgr.
- private int labelRes; // the label information from the package mgr.
- private int icon; // resource identifier of activity's icon.
- private int theme; // resource identifier of activity's theme.
+ private final int theme; // resource identifier of activity's theme.
private Task task; // the task this is in.
private long createTime = System.currentTimeMillis();
long lastVisibleTime; // last time this activity became visible
@@ -506,7 +503,7 @@
long launchTickTime; // base time for launch tick messages
long topResumedStateLossTime; // last time we reported top resumed state loss to an activity
// Last configuration reported to the activity in the client process.
- private MergedConfiguration mLastReportedConfiguration;
+ private final MergedConfiguration mLastReportedConfiguration;
private int mLastReportedDisplayId;
boolean mLastReportedMultiWindowMode;
boolean mLastReportedPictureInPictureMode;
@@ -565,7 +562,6 @@
boolean idle; // has the activity gone idle?
boolean hasBeenLaunched;// has this activity ever been launched?
boolean immersive; // immersive mode (don't interrupt if possible)
- boolean forceNewConfig; // force re-create with new config next time
boolean supportsEnterPipOnTaskSwitch; // This flag is set by the system to indicate that the
// activity can enter picture in picture while pausing (only when switching to another task)
// The PiP params used when deferring the entering of picture-in-picture.
@@ -684,6 +680,7 @@
private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
new WindowState.UpdateReportedVisibilityResults();
+ // TODO(b/317000737): Replace it with visibility states lookup.
int mTransitionChangeFlags;
/** Whether we need to setup the animation to animate only within the letterbox. */
@@ -948,7 +945,7 @@
private int mConfigurationSeq;
/**
- * Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
+ * Temp configs used in {@link #ensureActivityConfiguration()}
*/
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
@@ -1058,17 +1055,15 @@
pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
pw.print(prefix); pw.print("mActivityComponent=");
pw.println(mActivityComponent.flattenToShortString());
- if (info != null && info.applicationInfo != null) {
- final ApplicationInfo appInfo = info.applicationInfo;
- pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
- if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
- pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
- }
- pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
- if (appInfo.splitSourceDirs != null) {
- pw.print(prefix); pw.print("splitDir=");
- pw.println(Arrays.toString(appInfo.splitSourceDirs));
- }
+ final ApplicationInfo appInfo = info.applicationInfo;
+ pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+ if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+ pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+ }
+ pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
+ if (appInfo.splitSourceDirs != null) {
+ pw.print(prefix); pw.print("splitDir=");
+ pw.println(Arrays.toString(appInfo.splitSourceDirs));
}
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
pw.print(" componentSpecified="); pw.print(componentSpecified);
@@ -1079,8 +1074,6 @@
}
pw.print(prefix); pw.print("compat=");
pw.print(mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo));
- pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
- pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
pw.println(prefix + "mLastReportedConfigurations:");
mLastReportedConfiguration.dump(pw, prefix + " ");
@@ -1510,7 +1503,7 @@
updatePictureInPictureMode(null, false);
} else {
mLastReportedMultiWindowMode = inMultiWindowMode;
- ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS);
+ ensureActivityConfiguration();
}
}
}
@@ -1529,8 +1522,7 @@
// precede the configuration change from the resize.
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
- ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
- true /* ignoreVisibility */);
+ ensureActivityConfiguration(true /* ignoreVisibility */);
if (inPictureInPictureMode && findMainWindow() == null) {
// Prevent malicious app entering PiP without valid WindowState, which can in turn
// result a non-touchable PiP window since the InputConsumer for PiP requires it.
@@ -2158,14 +2150,6 @@
sConstrainDisplayApisConfig = new ConstrainDisplayApisConfig();
}
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
- nonLocalizedLabel = aInfo.nonLocalizedLabel;
- labelRes = aInfo.labelRes;
- if (nonLocalizedLabel == null && labelRes == 0) {
- ApplicationInfo app = aInfo.applicationInfo;
- nonLocalizedLabel = app.nonLocalizedLabel;
- labelRes = app.labelRes;
- }
- icon = aInfo.getIconResource();
theme = aInfo.getThemeResource();
if ((aInfo.flags & FLAG_MULTIPROCESS) != 0 && _caller != null
&& (aInfo.applicationInfo.uid == SYSTEM_UID
@@ -3106,7 +3090,7 @@
// {@link #returningOptions} of the activity under this one can be applied in
// {@link #handleAlreadyVisible()}.
if (changed || !occludesParent) {
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
}
return changed;
}
@@ -3746,8 +3730,8 @@
}
if (ensureVisibility) {
- mDisplayContent.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, true /* notifyClients */);
+ mDisplayContent.ensureActivitiesVisible(null /* starting */,
+ true /* notifyClients */);
}
}
@@ -4164,8 +4148,7 @@
if (rootTask != null && rootTask.shouldSleepOrShutDownActivities()) {
// Activity is always relaunched to either resumed or paused state. If it was
// relaunched while hidden (by keyguard or smth else), it should be stopped.
- rootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
+ rootTask.ensureActivitiesVisible(null /* starting */);
}
}
@@ -4680,14 +4663,12 @@
void setShowWhenLocked(boolean showWhenLocked) {
mShowWhenLocked = showWhenLocked;
- mAtmService.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */, false /* preserveWindows */);
+ mAtmService.mRootWindowContainer.ensureActivitiesVisible();
}
void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
mInheritShownWhenLocked = inheritShowWhenLocked;
- mAtmService.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */, false /* preserveWindows */);
+ mAtmService.mRootWindowContainer.ensureActivitiesVisible();
}
/**
@@ -5469,8 +5450,13 @@
// Defer committing visibility until transition starts.
if (isCollecting) {
// It may be occluded by the activity above that calls convertFromTranslucent().
- if (!visible && mTransitionController.inPlayingTransition(this)) {
- mTransitionChangeFlags |= FLAG_IS_OCCLUDED;
+ // Or it may be restoring transient launch to invisible when finishing transition.
+ if (!visible) {
+ if (mTransitionController.inPlayingTransition(this)) {
+ mTransitionChangeFlags |= FLAG_IS_OCCLUDED;
+ } else if (mTransitionController.inFinishingTransition(this)) {
+ mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
+ }
}
return;
}
@@ -6407,7 +6393,7 @@
}
mDisplayContent.handleActivitySizeCompatModeIfNeeded(this);
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
}
/**
@@ -7888,7 +7874,7 @@
void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames,
Configuration config) {
super.applyFixedRotationTransform(info, displayFrames, config);
- ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ ensureActivityConfiguration();
}
/**
@@ -7983,7 +7969,7 @@
startFreezingScreen(originalDisplayRotation);
// This activity may relaunch or perform configuration change so once it has reported drawn,
// the screen can be unfrozen.
- ensureActivityConfiguration(0 /* globalChanges */, !PRESERVE_WINDOWS);
+ ensureActivityConfiguration();
if (mTransitionController.isCollecting(this)) {
// In case the task was changed from PiP but still keeps old transform.
task.resetSurfaceControlTransforms();
@@ -8011,7 +7997,7 @@
// the request is handled at task level with letterbox.
if (!getMergedOverrideConfiguration().equals(
mLastReportedConfiguration.getMergedConfiguration())) {
- ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */,
+ ensureActivityConfiguration(
false /* ignoreVisibility */, true /* isRequestedOrientationChanged */);
if (mTransitionController.inPlayingTransition(this)) {
mTransitionController.mValidateActivityCompat.add(this);
@@ -8064,7 +8050,7 @@
*/
@Override
int getOrientation(int candidate) {
- if (shouldIgnoreOrientationRequests()) {
+ if (finishing || shouldIgnoreOrientationRequests()) {
return SCREEN_ORIENTATION_UNSET;
}
@@ -8078,8 +8064,7 @@
// The {@link ActivityRecord} should only specify an orientation when it is not closing.
// Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another
// task being started in the wrong orientation during the transition.
- if (!getDisplayContent().mClosingApps.contains(this)
- && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
+ if (isVisibleRequested()) {
return getOverrideOrientation();
}
@@ -9519,14 +9504,12 @@
return mLastReportedDisplayId != getDisplayId();
}
- boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
- return ensureActivityConfiguration(globalChanges, preserveWindow,
- false /* ignoreVisibility */, false /* isRequestedOrientationChanged */);
+ boolean ensureActivityConfiguration() {
+ return ensureActivityConfiguration(false /* ignoreVisibility */);
}
- boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
- boolean ignoreVisibility) {
- return ensureActivityConfiguration(globalChanges, preserveWindow, ignoreVisibility,
+ boolean ensureActivityConfiguration(boolean ignoreVisibility) {
+ return ensureActivityConfiguration(ignoreVisibility,
false /* isRequestedOrientationChanged */);
}
@@ -9534,9 +9517,6 @@
* Make sure the given activity matches the current configuration. Ensures the HistoryRecord
* is updated with the correct configuration and all other bookkeeping is handled.
*
- * @param globalChanges The changes to the global configuration.
- * @param preserveWindow If the activity window should be preserved on screen if the activity
- * is relaunched.
* @param ignoreVisibility If we should try to relaunch the activity even if it is invisible
* (stopped state). This is useful for the case where we know the
* activity will be visible soon and we want to ensure its configuration
@@ -9546,8 +9526,8 @@
* @return False if the activity was relaunched and true if it wasn't relaunched because we
* can't or the app handles the specific configuration that is changing.
*/
- boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
- boolean ignoreVisibility, boolean isRequestedOrientationChanged) {
+ boolean ensureActivityConfiguration(boolean ignoreVisibility,
+ boolean isRequestedOrientationChanged) {
final Task rootTask = getRootTask();
if (rootTask.mConfigWillChange) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
@@ -9600,7 +9580,7 @@
// configurations because there are cases (like moving a task to the root pinned task) where
// the combine configurations are equal, but would otherwise differ in the override config
mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
- if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
+ if (getConfiguration().equals(mTmpConfig) && !displayChanged) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+ "unchanged in %s", this);
return true;
@@ -9627,7 +9607,7 @@
return true;
}
- if (changes == 0 && !forceNewConfig) {
+ if (changes == 0) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s",
this);
// There are no significant differences, so we won't relaunch but should still deliver
@@ -9649,7 +9629,6 @@
// pick that up next time it starts.
if (!attachedToProcess()) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter not running %s", this);
- forceNewConfig = false;
return true;
}
@@ -9659,14 +9638,24 @@
Integer.toHexString(changes), Integer.toHexString(info.getRealConfigChanged()),
mLastReportedConfiguration);
- if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
+ if (shouldRelaunchLocked(changes, mTmpConfig)) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
- startFreezingScreenLocked(globalChanges);
- forceNewConfig = false;
+ if (mVisible && mAtmService.mTmpUpdateConfigurationResult.mIsUpdating
+ && !mTransitionController.isShellTransitionsEnabled()) {
+ startFreezingScreenLocked(mAtmService.mTmpUpdateConfigurationResult.changes);
+ }
+ final boolean displayMayChange = mTmpConfig.windowConfiguration.getDisplayRotation()
+ != getWindowConfiguration().getDisplayRotation()
+ || !mTmpConfig.windowConfiguration.getMaxBounds().equals(
+ getWindowConfiguration().getMaxBounds());
+ final boolean isAppResizeOnly = !displayMayChange
+ && (changes & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE
+ | CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)) == 0;
// Do not preserve window if it is freezing screen because the original window won't be
// able to update drawn state that causes freeze timeout.
- preserveWindow &= isResizeOnlyChange(changes) && !mFreezingScreen;
+ // TODO(b/258618073): Always preserve if possible.
+ final boolean preserveWindow = isAppResizeOnly && !mFreezingScreen;
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
if (hasResizeChange) {
final boolean isDragResizing = task.isDragResizing();
@@ -9830,11 +9819,6 @@
return changes;
}
- private static boolean isResizeOnlyChange(int change) {
- return (change & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
- | CONFIG_SCREEN_LAYOUT)) == 0;
- }
-
private static boolean hasResizeChange(int change) {
return (change & (CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
| CONFIG_SCREEN_LAYOUT)) != 0;
@@ -9878,12 +9862,9 @@
task.mTaskId, shortComponentName, Integer.toHexString(configChangeFlags));
}
- startFreezingScreenLocked(0);
-
try {
ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
(andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
- forceNewConfig = false;
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(token,
pendingResults, pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 86be6ba..7af494c 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -33,6 +33,7 @@
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
+import com.android.window.flags.Flags;
import java.io.File;
import java.util.ArrayList;
@@ -121,7 +122,8 @@
// TODO remove when enabled
static boolean isSnapshotEnabled() {
- return SystemProperties.getInt("persist.wm.debug.activity_screenshot", 0) != 0;
+ return SystemProperties.getInt("persist.wm.debug.activity_screenshot", 0) != 0
+ || Flags.activitySnapshotByDefault();
}
static PersistInfoProvider createPersistInfoProvider(
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1b45c1b..e7621ff 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -224,7 +224,7 @@
// before issuing the work challenge.
return true;
}
- if (interceptLockedManagedProfileIfNeeded()) {
+ if (interceptLockedProfileIfNeeded()) {
return true;
}
if (interceptHomeIfNeeded()) {
@@ -378,7 +378,7 @@
return true;
}
- private boolean interceptLockedManagedProfileIfNeeded() {
+ private boolean interceptLockedProfileIfNeeded() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 630b9e1..d90d017 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -73,7 +73,6 @@
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_DEFAULT;
import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
@@ -579,30 +578,11 @@
computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid),
realCallingPid);
if (resolveInfo == null) {
- final UserInfo userInfo = supervisor.getUserInfo(userId);
- if (userInfo != null && userInfo.isManagedProfile()) {
- // Special case for managed profiles, if attempting to launch non-cryto aware
- // app in a locked managed profile from an unlocked parent allow it to resolve
- // as user will be sent via confirm credentials to unlock the profile.
- final UserManager userManager = UserManager.get(supervisor.mService.mContext);
- boolean profileLockedAndParentUnlockingOrUnlocked = false;
- final long token = Binder.clearCallingIdentity();
- try {
- final UserInfo parent = userManager.getProfileParent(userId);
- profileLockedAndParentUnlockingOrUnlocked = (parent != null)
- && userManager.isUserUnlockingOrUnlocked(parent.id)
- && !userManager.isUserUnlockingOrUnlocked(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- if (profileLockedAndParentUnlockingOrUnlocked) {
- resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- computeResolveFilterUid(callingUid, realCallingUid,
- filterCallingUid), realCallingPid);
- }
- }
+ // Special case for profiles: If attempting to launch non-crypto aware app in a
+ // locked profile or launch an app in a profile that is stopped by quiet mode from
+ // an unlocked parent, allow it to resolve as user will be sent via confirm
+ // credentials to unlock the profile.
+ resolveInfo = resolveIntentForLockedOrStoppedProfiles(supervisor);
}
// Collect information about the target of the Intent.
@@ -616,6 +596,36 @@
UserHandle.getUserId(activityInfo.applicationInfo.uid));
}
}
+
+ /**
+ * Resolve intent for locked or stopped profiles if the parent profile is unlocking or
+ * unlocked.
+ */
+ ResolveInfo resolveIntentForLockedOrStoppedProfiles(
+ ActivityTaskSupervisor supervisor) {
+ final UserInfo userInfo = supervisor.getUserInfo(userId);
+ if (userInfo != null && userInfo.isProfile()) {
+ final UserManager userManager = UserManager.get(supervisor.mService.mContext);
+ boolean profileLockedAndParentUnlockingOrUnlocked = false;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final UserInfo parent = userManager.getProfileParent(userId);
+ profileLockedAndParentUnlockingOrUnlocked = (parent != null)
+ && userManager.isUserUnlockingOrUnlocked(parent.id)
+ && !userManager.isUserUnlockingOrUnlocked(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (profileLockedAndParentUnlockingOrUnlocked) {
+ return supervisor.resolveIntent(intent, resolvedType, userId,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ computeResolveFilterUid(callingUid, realCallingUid,
+ filterCallingUid), realCallingPid);
+ }
+ }
+ return null;
+ }
}
ActivityStarter(ActivityStartController controller, ActivityTaskManagerService service,
@@ -1722,6 +1732,7 @@
// So disallow the transient hide activity to move itself to front, e.g. trampoline.
if (!avoidMoveToFront() && (mService.mHomeProcess == null
|| mService.mHomeProcess.mUid != realCallingUid)
+ && (prevTopTask != null && prevTopTask.isActivityTypeHomeOrRecents())
&& r.mTransitionController.isTransientHide(targetTask)) {
mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
}
@@ -1848,8 +1859,7 @@
// over is removed.
// Passing {@code null} as the start parameter ensures all activities are made
// visible.
- mTargetRootTask.ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */, !PRESERVE_WINDOWS);
+ mTargetRootTask.ensureActivitiesVisible(null /* starting */);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
mTargetRootTask.mDisplayContent.executeAppTransition();
@@ -2767,10 +2777,7 @@
}
}
- // If the target task is not in the front, then we need to bring it to the front...
- // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
- // the same behavior as if a new instance was being started, which means not bringing it
- // to the front if the caller is not itself in the front.
+ // If the target task is not in the front, then we need to bring it to the front.
final boolean differentTopTask;
if (mTargetRootTask.getDisplayArea() == mPreferredTaskDisplayArea) {
final Task focusRootTask = mTargetRootTask.mDisplayContent.getFocusedRootTask();
@@ -2787,49 +2794,47 @@
if (differentTopTask && !avoidMoveToFront()) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- if (mSourceRecord == null || inTopNonFinishingTask(mSourceRecord)) {
- // We really do want to push this one into the user's face, right now.
- if (mLaunchTaskBehind && mSourceRecord != null) {
- intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
- }
-
- if (intentActivity.isDescendantOf(mTargetRootTask)) {
- // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
- // tasks hierarchies.
- if (mTargetRootTask != intentTask
- && mTargetRootTask != intentTask.getParent().asTask()) {
- intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
- false /* includingParents */);
- intentTask = intentTask.getParent().asTaskFragment().getTask();
- }
- // If the activity is visible in multi-windowing mode, it may already be on
- // the top (visible to user but not the global top), then the result code
- // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT.
- final boolean wasTopOfVisibleRootTask = intentActivity.isVisibleRequested()
- && intentActivity.inMultiWindowMode()
- && intentActivity == mTargetRootTask.topRunningActivity()
- && !intentActivity.mTransitionController.isTransientHide(
- mTargetRootTask);
- // We only want to move to the front, if we aren't going to launch on a
- // different root task. If we launch on a different root task, we will put the
- // task on top there.
- // Defer resuming the top activity while moving task to top, since the
- // current task-top activity may not be the activity that should be resumed.
- mTargetRootTask.moveTaskToFront(intentTask, mNoAnimation, mOptions,
- mStartActivity.appTimeTracker, DEFER_RESUME,
- "bringingFoundTaskToFront");
- mMovedToFront = !wasTopOfVisibleRootTask;
- } else if (intentActivity.getWindowingMode() != WINDOWING_MODE_PINNED) {
- // Leaves reparenting pinned task operations to task organizer to make sure it
- // dismisses pinned task properly.
- // TODO(b/199997762): Consider leaving all reparent operation of organized tasks
- // to task organizer.
- intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
- ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
- mMovedToFront = true;
- }
- mOptions = null;
+ // We really do want to push this one into the user's face, right now.
+ if (mLaunchTaskBehind && mSourceRecord != null) {
+ intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
+
+ if (intentActivity.isDescendantOf(mTargetRootTask)) {
+ // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
+ // tasks hierarchies.
+ if (mTargetRootTask != intentTask
+ && mTargetRootTask != intentTask.getParent().asTask()) {
+ intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
+ false /* includingParents */);
+ intentTask = intentTask.getParent().asTaskFragment().getTask();
+ }
+ // If the activity is visible in multi-windowing mode, it may already be on
+ // the top (visible to user but not the global top), then the result code
+ // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT.
+ final boolean wasTopOfVisibleRootTask = intentActivity.isVisibleRequested()
+ && intentActivity.inMultiWindowMode()
+ && intentActivity == mTargetRootTask.topRunningActivity()
+ && !intentActivity.mTransitionController.isTransientHide(
+ mTargetRootTask);
+ // We only want to move to the front, if we aren't going to launch on a
+ // different root task. If we launch on a different root task, we will put the
+ // task on top there.
+ // Defer resuming the top activity while moving task to top, since the
+ // current task-top activity may not be the activity that should be resumed.
+ mTargetRootTask.moveTaskToFront(intentTask, mNoAnimation, mOptions,
+ mStartActivity.appTimeTracker, DEFER_RESUME,
+ "bringingFoundTaskToFront");
+ mMovedToFront = !wasTopOfVisibleRootTask;
+ } else if (intentActivity.getWindowingMode() != WINDOWING_MODE_PINNED) {
+ // Leaves reparenting pinned task operations to task organizer to make sure it
+ // dismisses pinned task properly.
+ // TODO(b/199997762): Consider leaving all reparent operation of organized tasks
+ // to task organizer.
+ intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+ ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
+ mMovedToFront = true;
+ }
+ mOptions = null;
}
if (differentTopTask) {
logPIOnlyCreatorAllowsBAL();
@@ -2850,20 +2855,6 @@
mRootWindowContainer.getDefaultTaskDisplayArea(), mTargetRootTask);
}
- private boolean inTopNonFinishingTask(ActivityRecord r) {
- if (r == null || r.getTask() == null) {
- return false;
- }
-
- final Task rTask = r.getTask();
- final Task parent = rTask.getCreatedByOrganizerTask() != null
- ? rTask.getCreatedByOrganizerTask() : r.getRootTask();
- final ActivityRecord topNonFinishingActivity = parent != null
- ? parent.getTopNonFinishingActivity() : null;
-
- return topNonFinishingActivity != null && topNonFinishingActivity.getTask() == rTask;
- }
-
private void resumeTargetRootTaskIfNeeded() {
if (mDoResume) {
final ActivityRecord next = mTargetRootTask.topRunningActivity(
@@ -2875,7 +2866,7 @@
mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, null,
mOptions, mTransientLaunch);
} else {
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
}
} else {
ActivityOptions.abort(mOptions);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 908c49e..f43c1b0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -62,6 +62,7 @@
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
import static android.provider.Settings.System.FONT_SCALE;
+import static android.service.controls.flags.Flags.homePanelDream;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -117,7 +118,6 @@
import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
@@ -495,16 +495,13 @@
final UpdateConfigurationResult mTmpUpdateConfigurationResult =
new UpdateConfigurationResult();
+ // TODO(b/258618073): Remove this and make the related methods return whether config is changed.
static final class UpdateConfigurationResult {
// Configuration changes that were updated.
int changes;
// If the activity was relaunched to match the new configuration.
boolean activityRelaunched;
-
- void reset() {
- changes = 0;
- activityRelaunched = false;
- }
+ boolean mIsUpdating;
}
/** Current sequencing integer of the configuration, for skipping old configurations. */
@@ -1501,14 +1498,19 @@
a.exported = true;
a.name = DreamActivity.class.getName();
a.enabled = true;
- a.launchMode = ActivityInfo.LAUNCH_SINGLE_INSTANCE;
a.persistableMode = ActivityInfo.PERSIST_NEVER;
a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
- a.resizeMode = RESIZE_MODE_UNRESIZEABLE;
a.configChanges = 0xffffffff;
+ if (homePanelDream()) {
+ a.launchMode = ActivityInfo.LAUNCH_SINGLE_TASK;
+ } else {
+ a.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ a.launchMode = ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+ }
+
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
@@ -3828,8 +3830,7 @@
Settings.System.clearConfiguration(values);
}
updateConfigurationLocked(values, null, false, false /* persistent */,
- UserHandle.USER_NULL, false /* deferResume */,
- mTmpUpdateConfigurationResult);
+ UserHandle.USER_NULL, false /* deferResume */);
return mTmpUpdateConfigurationResult.changes != 0;
} finally {
Binder.restoreCallingIdentity(origId);
@@ -4501,12 +4502,6 @@
}
}
- private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean persistent, int userId, boolean deferResume) {
- return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
- deferResume, null /* result */);
- }
-
/**
* Do either or both things: (1) change the current configuration, and (2)
* make sure the given activity is running with the (now) current
@@ -4518,8 +4513,7 @@
* for that particular user
*/
boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean persistent, int userId, boolean deferResume,
- ActivityTaskManagerService.UpdateConfigurationResult result) {
+ boolean initLocale, boolean persistent, int userId, boolean deferResume) {
int changes = 0;
boolean kept = true;
@@ -4527,19 +4521,18 @@
try {
if (values != null) {
changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId);
+ mTmpUpdateConfigurationResult.changes = changes;
+ mTmpUpdateConfigurationResult.mIsUpdating = true;
}
if (!deferResume) {
kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
}
} finally {
+ mTmpUpdateConfigurationResult.mIsUpdating = false;
continueWindowLayout();
}
-
- if (result != null) {
- result.changes = changes;
- result.activityRelaunched = !kept;
- }
+ mTmpUpdateConfigurationResult.activityRelaunched = !kept;
return kept;
}
@@ -5319,12 +5312,10 @@
}
if (starting != null) {
- kept = starting.ensureActivityConfiguration(changes,
- false /* preserveWindow */);
+ kept = starting.ensureActivityConfiguration();
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
- mRootWindowContainer.ensureActivitiesVisible(starting, changes,
- !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible(starting);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e59601c..1872f6e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -906,7 +906,6 @@
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
- r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
// Because we could be starting an Activity in the system process this may not go
@@ -1463,7 +1462,7 @@
}
mLaunchingActivityWakeLock.release();
}
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
}
// Atomically retrieve all of the other things to do.
@@ -1604,7 +1603,7 @@
*/
rootTask.cancelAnimation();
rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
- rootTask.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ rootTask.ensureActivitiesVisible(null /* starting */);
activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
true /* processPausingActivities */, null /* configuration */);
@@ -1623,7 +1622,7 @@
// Follow on the workaround: activities are kept force hidden till the new windowing
// mode is set.
rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
- mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
mRootWindowContainer.resumeFocusedTasksTopActivities();
} finally {
mService.continueWindowLayout();
@@ -2027,7 +2026,7 @@
final Task rootTask = r.getRootTask();
if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
// Make sure activity & window visibility should be identical
// for all displays in this stage.
mRootWindowContainer.executeAppTransitionForAllDisplay();
@@ -2043,7 +2042,7 @@
mRecentTasks.add(task);
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
- rootTask.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ rootTask.ensureActivitiesVisible(null /* starting */);
// When launching tasks behind, update the last active time of the top task after the new
// task has been shown briefly
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 68d13cd..6ed8967 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -91,6 +91,13 @@
/** Non-zero if this controller is triggered by shell transition. */
private final @TransitionOp int mTransitionOp;
+ /**
+ * Whether {@link #setupStartTransaction} is called when the transition is ready.
+ * If this is never set for {@link #OP_CHANGE}, the display may be changed to original state
+ * before the transition is ready, then this controller should be finished.
+ */
+ private boolean mIsStartTransactionPrepared;
+
/** Whether the start transaction of the transition is committed (by shell). */
private boolean mIsStartTransactionCommitted;
@@ -226,7 +233,8 @@
void updateTargetWindows() {
if (mTransitionOp == OP_LEGACY) return;
if (!mIsStartTransactionCommitted) {
- if (mTimeoutRunnable == null && !mDisplayContent.hasTopFixedRotationLaunchingApp()
+ if ((mTimeoutRunnable == null || !mIsStartTransactionPrepared)
+ && !mDisplayContent.hasTopFixedRotationLaunchingApp()
&& !mDisplayContent.isRotationChanging() && !mDisplayContent.inTransition()) {
Slog.d(TAG, "Cancel for no change");
mDisplayContent.finishAsyncRotationIfPossible();
@@ -401,9 +409,18 @@
if (mTimeoutRunnable == null) {
mTimeoutRunnable = () -> {
synchronized (mService.mGlobalLock) {
- Slog.i(TAG, "Async rotation timeout: " + (!mIsStartTransactionCommitted
- ? " start transaction is not committed" : mTargetWindowTokens));
+ final String reason;
if (!mIsStartTransactionCommitted) {
+ if (!mIsStartTransactionPrepared) {
+ reason = "setupStartTransaction is not called";
+ } else {
+ reason = "start transaction is not committed";
+ }
+ } else {
+ reason = "unfinished windows " + mTargetWindowTokens;
+ }
+ Slog.i(TAG, "Async rotation timeout: " + reason);
+ if (!mIsStartTransactionCommitted && mIsStartTransactionPrepared) {
// The transaction commit timeout will be handled by:
// 1. BLASTSyncEngine will notify onTransactionCommitTimeout() and then
// apply the start transaction of transition.
@@ -558,6 +575,7 @@
}
}
});
+ mIsStartTransactionPrepared = true;
}
/** Called when the start transition is ready, but it is not applied in time. */
@@ -577,6 +595,10 @@
/** Called when the transition by shell is done. */
void onTransitionFinished() {
if (mTransitionOp == OP_CHANGE) {
+ if (mTargetWindowTokens.isEmpty()) {
+ // If nothing was handled, then complete with the transition.
+ mDisplayContent.finishAsyncRotationIfPossible();
+ }
// With screen rotation animation, the windows are always faded in when they are drawn.
// Because if they are drawn fast enough, the fade animation should not be observable.
return;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c3f1e41..22d17b5 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1614,7 +1614,7 @@
"Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity);
activity.mTaskSupervisor.mStoppingActivities.remove(activity);
activity.getDisplayContent().ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */, false /* preserveWindows */, true);
+ true /* notifyClients */);
}
private static void restoreLaunchBehind(@NonNull ActivityRecord activity) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4929df80..eed46fe 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -36,6 +36,7 @@
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -152,36 +153,25 @@
static final int BAL_ALLOW_SDK_SANDBOX = 10;
static String balCodeToString(@BalCode int balCode) {
- switch (balCode) {
- case BAL_ALLOW_ALLOWLISTED_COMPONENT:
- return "BAL_ALLOW_ALLOWLISTED_COMPONENT";
- case BAL_ALLOW_ALLOWLISTED_UID:
- return "BAL_ALLOW_ALLOWLISTED_UID";
- case BAL_ALLOW_DEFAULT:
- return "BAL_ALLOW_DEFAULT";
- case BAL_ALLOW_FOREGROUND:
- return "BAL_ALLOW_FOREGROUND";
- case BAL_ALLOW_GRACE_PERIOD:
- return "BAL_ALLOW_GRACE_PERIOD";
- case BAL_ALLOW_PENDING_INTENT:
- return "BAL_ALLOW_PENDING_INTENT";
- case BAL_ALLOW_PERMISSION:
- return "BAL_ALLOW_PERMISSION";
- case BAL_ALLOW_SAW_PERMISSION:
- return "BAL_ALLOW_SAW_PERMISSION";
- case BAL_ALLOW_SDK_SANDBOX:
- return "BAL_ALLOW_SDK_SANDBOX";
- case BAL_ALLOW_VISIBLE_WINDOW:
- return "BAL_ALLOW_VISIBLE_WINDOW";
- case BAL_BLOCK:
- return "BAL_BLOCK";
- default:
- throw new IllegalArgumentException("Unexpected value: " + balCode);
- }
+ return switch (balCode) {
+ case BAL_ALLOW_ALLOWLISTED_COMPONENT -> "BAL_ALLOW_ALLOWLISTED_COMPONENT";
+ case BAL_ALLOW_ALLOWLISTED_UID -> "BAL_ALLOW_ALLOWLISTED_UID";
+ case BAL_ALLOW_DEFAULT -> "BAL_ALLOW_DEFAULT";
+ case BAL_ALLOW_FOREGROUND -> "BAL_ALLOW_FOREGROUND";
+ case BAL_ALLOW_GRACE_PERIOD -> "BAL_ALLOW_GRACE_PERIOD";
+ case BAL_ALLOW_PENDING_INTENT -> "BAL_ALLOW_PENDING_INTENT";
+ case BAL_ALLOW_PERMISSION -> "BAL_ALLOW_PERMISSION";
+ case BAL_ALLOW_SAW_PERMISSION -> "BAL_ALLOW_SAW_PERMISSION";
+ case BAL_ALLOW_SDK_SANDBOX -> "BAL_ALLOW_SDK_SANDBOX";
+ case BAL_ALLOW_VISIBLE_WINDOW -> "BAL_ALLOW_VISIBLE_WINDOW";
+ case BAL_BLOCK -> "BAL_BLOCK";
+ default -> throw new IllegalArgumentException("Unexpected value: " + balCode);
+ };
}
@GuardedBy("mService.mGlobalLock")
- private HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity = new HashMap<>();
+ private final HashMap<Integer, FinishedActivityEntry> mTaskIdToFinishedActivity =
+ new HashMap<>();
@GuardedBy("mService.mGlobalLock")
private FinishedActivityEntry mTopFinishedActivity = null;
@@ -467,9 +457,8 @@
return !blocks();
}
- BalVerdict setOnlyCreatorAllows(boolean onlyCreatorAllows) {
+ void setOnlyCreatorAllows(boolean onlyCreatorAllows) {
mOnlyCreatorAllows = onlyCreatorAllows;
- return this;
}
boolean onlyCreatorAllows() {
@@ -481,10 +470,6 @@
return this;
}
- private boolean isBasedOnRealCaller() {
- return mBasedOnRealCaller;
- }
-
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(balCodeToString(mCode));
@@ -583,15 +568,14 @@
BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
if (!state.hasRealCaller()) {
- BalVerdict resultForRealCaller = null; // nothing to compute
if (resultForCaller.allows()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed. "
- + state.dump(resultForCaller, resultForRealCaller));
+ + state.dump(resultForCaller, resultForCaller));
}
return statsLog(resultForCaller, state);
}
- return abortLaunch(state, resultForCaller, resultForRealCaller);
+ return abortLaunch(state, resultForCaller, resultForCaller);
}
// The realCaller result is only calculated for PendingIntents (indicated by a valid
@@ -653,7 +637,7 @@
+ " if the PI creator upgrades target_sdk to 35+"
+ " AND the PI sender upgrades target_sdk to 34+! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
+ showBalRiskToast();
// return the realCaller result for backwards compatibility
return statsLog(resultForRealCaller, state);
}
@@ -679,7 +663,7 @@
+ " if the PI creator upgrades target_sdk to 35+! "
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
+ showBalRiskToast();
return statsLog(resultForCaller, state);
}
Slog.wtf(TAG,
@@ -696,7 +680,7 @@
+ " if the PI sender upgrades target_sdk to 34+! "
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalRiskToast("BAL would be blocked", state);
+ showBalRiskToast();
return statsLog(resultForRealCaller, state);
}
Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
@@ -712,7 +696,7 @@
BalVerdict resultForRealCaller) {
Slog.w(TAG, "Background activity launch blocked! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalBlockedToast("BAL blocked", state);
+ showBalBlockedToast();
return statsLog(BalVerdict.BLOCK, state);
}
@@ -910,7 +894,7 @@
/**
* Check if the app allows BAL.
- *
+ * <p>
* See {@link BackgroundLaunchProcessController#areBackgroundActivityStartsAllowed(int, int,
* String, int, boolean, boolean, boolean, long, long, long)} for details on the
* exceptions.
@@ -1104,19 +1088,15 @@
return true;
}
- private void showBalBlockedToast(String toastText, BalState state) {
+ private void showBalBlockedToast() {
if (balShowToastsBlocked()) {
- showToast(toastText
- + " caller:" + state.mCallingPackage
- + " realCaller:" + state.mRealCallingPackage);
+ showToast("BAL blocked. go/debug-bal");
}
}
- private void showBalRiskToast(String toastText, BalState state) {
+ private void showBalRiskToast() {
if (balShowToasts()) {
- showToast(toastText
- + " caller:" + state.mCallingPackage
- + " realCaller:" + state.mRealCallingPackage);
+ showToast("BAL allowed in compat mode. go/debug-bal");
}
}
@@ -1281,7 +1261,7 @@
* 2. Or top of an adjacent task fragment to (1)
* <p>
* The 'sourceRecord' can be considered top even if it is 'finishing'
- *
+ * <p>
* Returns a class where the elements are:
* <pre>
* shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
@@ -1344,7 +1324,7 @@
/**
* Determines if a source is allowed to add or remove activities from the task,
* if the current ActivityRecord is above it in the stack
- *
+ * <p>
* A transition is blocked ({@code false} returned) if all of the following are met:
* <pre>
* 1. The source activity and the current activity record belong to different apps
@@ -1489,8 +1469,8 @@
if (code == BAL_ALLOW_PENDING_INTENT
&& (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
- String activityName =
- intent != null ? intent.getComponent().flattenToShortString() : "";
+ String activityName = intent != null
+ ? requireNonNull(intent.getComponent()).flattenToShortString() : "";
FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
activityName,
BAL_ALLOW_PENDING_INTENT,
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 2e47677..c7df83a 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -24,6 +24,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.Slog;
@@ -74,13 +75,13 @@
}
/**
- * Similar to {@link #scheduleTransactionItem}, but is called without WM lock.
+ * Similar to {@link #scheduleTransactionItem}, but it sends the transaction immediately and
+ * it can be called without WM lock.
*
* @see WindowProcessController#setReportedProcState(int)
*/
- void scheduleTransactionItemUnlocked(@NonNull IApplicationThread client,
+ void scheduleTransactionItemNow(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
- // Immediately dispatching to client, and must not access WMS.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
if (transactionItem.isActivityLifecycleItem()) {
clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
@@ -146,9 +147,10 @@
/** Executes all the pending transactions. */
void dispatchPendingTransactions() {
- if (!Flags.bundleClientTransactionFlag()) {
+ if (!Flags.bundleClientTransactionFlag() || mPendingTransactions.isEmpty()) {
return;
}
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionsDispatched");
final int size = mPendingTransactions.size();
for (int i = 0; i < size; i++) {
final ClientTransaction transaction = mPendingTransactions.valueAt(i);
@@ -159,6 +161,7 @@
}
}
mPendingTransactions.clear();
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
/**
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 73bcc8d..1a8927e 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -19,7 +19,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
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.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_FIRST;
import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_LAST;
@@ -47,6 +46,7 @@
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
import com.android.internal.protolog.common.ProtoLog;
@@ -60,6 +60,7 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -347,6 +348,7 @@
private GameManagerInternal mGameManager;
private final AtomicFile mFile;
private final HashMap<String, Integer> mPackages = new HashMap<>();
+ private final SparseBooleanArray mLegacyScreenCompatPackages = new SparseBooleanArray();
private final CompatHandler mHandler;
private final SparseArray<CompatScaleProvider> mProviders = new SparseArray<>();
@@ -427,6 +429,7 @@
mPackages.remove(packageName);
scheduleWrite();
}
+ mLegacyScreenCompatPackages.delete(packageName.hashCode());
}
public void handlePackageAddedLocked(String packageName, boolean updated) {
@@ -458,6 +461,17 @@
mHandler.sendMessageDelayed(msg, 10000);
}
+ /**
+ * Returns {@code true} if the windows belonging to the package should be scaled with
+ * {@link DisplayContent#mCompatibleScreenScale}.
+ */
+ boolean useLegacyScreenCompatMode(String packageName) {
+ if (mLegacyScreenCompatPackages.size() == 0) {
+ return false;
+ }
+ return mLegacyScreenCompatPackages.get(packageName.hashCode());
+ }
+
public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
final boolean forceCompat = getPackageCompatModeEnabledLocked(ai);
final CompatScale compatScale = getCompatScaleFromProvider(ai.packageName, ai.uid);
@@ -466,8 +480,18 @@
: getCompatScale(ai.packageName, ai.uid, /* checkProvider= */ false);
final float densityScale = compatScale != null ? compatScale.mDensityScaleFactor : appScale;
final Configuration config = mService.getGlobalConfiguration();
- return new CompatibilityInfo(ai, config.screenLayout, config.smallestScreenWidthDp,
- forceCompat, appScale, densityScale);
+ final CompatibilityInfo info = new CompatibilityInfo(ai, config.screenLayout,
+ config.smallestScreenWidthDp, forceCompat, appScale, densityScale);
+ // Ignore invalid info which may be a placeholder of isolated process.
+ if (ai.flags != 0 && ai.sourceDir != null) {
+ if (!info.supportsScreen() && !"android".equals(ai.packageName)) {
+ Slog.i(TAG, "Use legacy screen compat mode: " + ai.packageName);
+ mLegacyScreenCompatPackages.put(ai.packageName.hashCode(), true);
+ } else if (mLegacyScreenCompatPackages.size() > 0) {
+ mLegacyScreenCompatPackages.delete(ai.packageName.hashCode());
+ }
+ }
+ return info;
}
float getCompatScale(String packageName, int uid) {
@@ -718,14 +742,23 @@
scheduleWrite();
- final Task rootTask = mService.getTopDisplayFocusedRootTask();
- ActivityRecord starting = rootTask.restartPackage(packageName);
-
+ final ArrayList<WindowProcessController> restartedApps = new ArrayList<>();
+ mService.mRootWindowContainer.forAllWindows(w -> {
+ final ActivityRecord ar = w.mActivityRecord;
+ if (ar != null) {
+ if (ar.packageName.equals(packageName) && !restartedApps.contains(ar.app)) {
+ ar.restartProcessIfVisible();
+ restartedApps.add(ar.app);
+ }
+ } else if (w.getProcess().mInfo.packageName.equals(packageName)) {
+ w.updateGlobalScale();
+ }
+ }, true /* traverseTopToBottom */);
// Tell all processes that loaded this package about the change.
SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap();
for (int i = pidMap.size() - 1; i >= 0; i--) {
final WindowProcessController app = pidMap.valueAt(i);
- if (!app.containsPackage(packageName)) {
+ if (!app.containsPackage(packageName) || restartedApps.contains(app)) {
continue;
}
try {
@@ -737,14 +770,6 @@
} catch (Exception e) {
}
}
-
- if (starting != null) {
- starting.ensureActivityConfiguration(0 /* globalChanges */,
- false /* preserveWindow */);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- rootTask.ensureActivitiesVisible(starting, 0, !PRESERVE_WINDOWS);
- }
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c98280e..e7ecf52 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -512,7 +512,11 @@
*/
private final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics();
- /** The desired scaling factor for compatible apps. */
+ /**
+ * The desired scaling factor for compatible apps. It limits the size of the window to be
+ * original size ([320x480] x density). Used to scale window for applications running under
+ * legacy compatibility mode.
+ */
float mCompatibleScreenScale;
/** @see #getCurrentOverrideConfigurationChanges */
@@ -775,7 +779,7 @@
/**
* Used to prevent recursions when calling
- * {@link #ensureActivitiesVisible(ActivityRecord, int, boolean, boolean)}
+ * {@link #ensureActivitiesVisible(ActivityRecord, boolean)}
*/
private boolean mInEnsureActivitiesVisible = false;
@@ -1709,7 +1713,7 @@
if (handled && requestingContainer instanceof ActivityRecord) {
final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
final boolean kept = updateDisplayOverrideConfigurationLocked(config, activityRecord,
- false /* deferResume */, null /* result */);
+ false /* deferResume */);
if (!kept) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
@@ -1717,7 +1721,7 @@
// We have a new configuration to push so we need to update ATMS for now.
// TODO: Clean up display configuration push between ATMS and WMS after unification.
updateDisplayOverrideConfigurationLocked(config, null /* starting */,
- false /* deferResume */, null);
+ false /* deferResume */);
}
return handled;
}
@@ -6329,7 +6333,7 @@
Settings.System.clearConfiguration(values);
updateDisplayOverrideConfigurationLocked(values, null /* starting */,
- false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
+ false /* deferResume */);
return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
}
@@ -6338,8 +6342,7 @@
* new one will be computed in WM based on current display info.
*/
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
- ActivityRecord starting, boolean deferResume,
- ActivityTaskManagerService.UpdateConfigurationResult result) {
+ ActivityRecord starting, boolean deferResume) {
int changes = 0;
boolean kept = true;
@@ -6357,19 +6360,19 @@
} else {
changes = performDisplayOverrideConfigUpdate(values);
}
+ mAtmService.mTmpUpdateConfigurationResult.changes = changes;
+ mAtmService.mTmpUpdateConfigurationResult.mIsUpdating = true;
}
if (!deferResume) {
kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
}
} finally {
+ mAtmService.mTmpUpdateConfigurationResult.mIsUpdating = false;
mAtmService.continueWindowLayout();
}
- if (result != null) {
- result.changes = changes;
- result.activityRelaunched = !kept;
- }
+ mAtmService.mTmpUpdateConfigurationResult.activityRelaunched = !kept;
return kept;
}
@@ -6565,8 +6568,7 @@
}
- void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
+ void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
if (mInEnsureActivitiesVisible) {
// Don't do recursive work.
return;
@@ -6575,8 +6577,7 @@
try {
mInEnsureActivitiesVisible = true;
forAllRootTasks(rootTask -> {
- rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
- notifyClients);
+ rootTask.ensureActivitiesVisible(starting, notifyClients);
});
if (mTransitionController.useShellTransitionsRotation()
&& mTransitionController.isCollecting()
@@ -6615,7 +6616,7 @@
if (!wasTransitionSet) {
prepareAppTransition(TRANSIT_NONE);
}
- mRootWindowContainer.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ mRootWindowContainer.ensureActivitiesVisible();
// If there was a transition set already we don't want to interfere with it as we might be
// starting it too early.
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 9cc311d..f40eb24 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -33,8 +33,6 @@
private boolean mAboveTop;
private boolean mContainerShouldBeVisible;
private boolean mBehindFullyOccludedContainer;
- private int mConfigChanges;
- private boolean mPreserveWindows;
private boolean mNotifyClients;
EnsureActivitiesVisibleHelper(TaskFragment container) {
@@ -45,14 +43,10 @@
* Update all attributes except {@link mTaskFragment} to use in subsequent calculations.
*
* @param starting The activity that is being started
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating.
* @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
* be sent to the clients.
*/
- void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
- boolean notifyClients) {
+ void reset(ActivityRecord starting, boolean notifyClients) {
mStarting = starting;
mTopRunningActivity = mTaskFragment.topRunningActivity();
// If the top activity is not fullscreen, then we need to make sure any activities under it
@@ -60,33 +54,26 @@
mAboveTop = mTopRunningActivity != null;
mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting);
mBehindFullyOccludedContainer = !mContainerShouldBeVisible;
- mConfigChanges = configChanges;
- mPreserveWindows = preserveWindows;
mNotifyClients = notifyClients;
}
/**
* Update and commit visibility with an option to also update the configuration of visible
* activities.
- * @see Task#ensureActivitiesVisible(ActivityRecord, int, boolean)
- * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+ * @see Task#ensureActivitiesVisible(ActivityRecord)
+ * @see RootWindowContainer#ensureActivitiesVisible()
* @param starting The top most activity in the task.
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
*
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating.
* @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
* be sent to the clients.
*/
- void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
- boolean notifyClients) {
- reset(starting, configChanges, preserveWindows, notifyClients);
+ void process(@Nullable ActivityRecord starting, boolean notifyClients) {
+ reset(starting, notifyClients);
if (DEBUG_VISIBILITY) {
- Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTopRunningActivity
- + " configChanges=0x" + Integer.toHexString(configChanges));
+ Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTopRunningActivity);
}
if (mTopRunningActivity != null && mTaskFragment.asTask() != null) {
// TODO(14709632): Check if this needed to be implemented in TaskFragment.
@@ -107,8 +94,7 @@
final TaskFragment childTaskFragment = child.asTaskFragment();
if (childTaskFragment != null
&& childTaskFragment.getTopNonFinishingActivity() != null) {
- childTaskFragment.updateActivityVisibilities(starting, configChanges,
- preserveWindows, notifyClients);
+ childTaskFragment.updateActivityVisibilities(starting, notifyClients);
// The TaskFragment should fully occlude the activities below if the bounds
// equals to its parent task, unless it is translucent.
mBehindFullyOccludedContainer |=
@@ -188,13 +174,11 @@
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != mStarting && mNotifyClients) {
- r.ensureActivityConfiguration(0 /* globalChanges */, mPreserveWindows,
- true /* ignoreVisibility */);
+ r.ensureActivityConfiguration(true /* ignoreVisibility */);
}
if (!r.attachedToProcess()) {
- makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges,
- resumeTopActivity && isTop, r);
+ makeVisibleAndRestartIfNeeded(mStarting, resumeTopActivity && isTop, r);
} else if (r.isVisibleRequested()) {
// If this activity is already visible, then there is nothing to do here.
if (DEBUG_VISIBILITY) {
@@ -213,8 +197,6 @@
} else {
r.makeVisibleIfNeeded(mStarting, mNotifyClients);
}
- // Aggregate current change flags.
- mConfigChanges |= r.configChangeFlags;
} else {
if (DEBUG_VISIBILITY) {
Slog.v(TAG_VISIBILITY, "Make invisible? " + r
@@ -242,16 +224,13 @@
}
}
- private void makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
+ private void makeVisibleAndRestartIfNeeded(ActivityRecord starting,
boolean andResume, ActivityRecord r) {
// This activity needs to be visible, but isn't even running...
// get it started and resume if no other root task in this root task is resumed.
if (DEBUG_VISIBILITY) {
Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
}
- if (r != starting) {
- r.startFreezingScreenLocked(configChanges);
- }
if (!r.isVisibleRequested() || r.mLaunchTaskBehind) {
if (DEBUG_VISIBILITY) {
Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 8cf4713..a84ebd9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -167,10 +167,10 @@
/** {@inheritDoc} */
@Override
- public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
- int policyFlags) {
+ public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+ long whenNanos, int policyFlags) {
return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
- displayId, whenNanos, policyFlags);
+ displayId, source, action, whenNanos, policyFlags);
}
/**
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index cbc7b83..6d11804 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -43,7 +43,6 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
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.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_GOING_AWAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY;
@@ -239,7 +238,7 @@
// Update the sleep token first such that ensureActivitiesVisible has correct sleep token
// state when evaluating visibilities.
updateKeyguardSleepToken();
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */,
displayId);
setWakeTransitionReady();
@@ -291,7 +290,7 @@
// Some stack visibility might change (e.g. docked stack)
mRootWindowContainer.resumeFocusedTasksTopActivities();
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
mRootWindowContainer.addStartingWindowsForVisibleActivities();
mWindowManager.executeAppTransition();
} finally {
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9305396..68bd326 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -46,6 +46,7 @@
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
@@ -172,7 +173,7 @@
// Corresponds to OVERRIDE_ANY_ORIENTATION
private final boolean mIsOverrideAnyOrientationEnabled;
// Corresponds to OVERRIDE_ANY_ORIENTATION_TO_USER
- private final boolean mIsOverrideToUserOrientationEnabled;
+ private final boolean mIsSystemOverrideToFullscreenEnabled;
// Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
private final boolean mIsOverrideToPortraitOrientationEnabled;
// Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
@@ -357,7 +358,7 @@
PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
- mIsOverrideToUserOrientationEnabled =
+ mIsSystemOverrideToFullscreenEnabled =
isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER);
mIsOverrideToPortraitOrientationEnabled =
isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT);
@@ -670,8 +671,7 @@
final DisplayContent displayContent = mActivityRecord.mDisplayContent;
final boolean isIgnoreOrientationRequestEnabled = displayContent != null
&& displayContent.getIgnoreOrientationRequest();
- if (shouldApplyUserFullscreenOverride()
- && isIgnoreOrientationRequestEnabled) {
+ if (shouldApplyUserFullscreenOverride() && isIgnoreOrientationRequestEnabled) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_USER)
@@ -706,8 +706,7 @@
// mUserAspectRatio is always initialized first in shouldApplyUserFullscreenOverride(),
// which will always come first before this check as user override > device
// manufacturer override.
- if (mUserAspectRatio == PackageManager.USER_MIN_ASPECT_RATIO_UNSET
- && mIsOverrideToUserOrientationEnabled && isIgnoreOrientationRequestEnabled) {
+ if (isSystemOverrideToFullscreenEnabled() && isIgnoreOrientationRequestEnabled) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_USER));
@@ -1185,6 +1184,7 @@
mUserAspectRatio = getUserMinAspectRatioOverrideCode();
return mUserAspectRatio != USER_MIN_ASPECT_RATIO_UNSET
+ && mUserAspectRatio != USER_MIN_ASPECT_RATIO_APP_DEFAULT
&& mUserAspectRatio != USER_MIN_ASPECT_RATIO_FULLSCREEN;
}
@@ -1200,6 +1200,13 @@
return mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN;
}
+ boolean isSystemOverrideToFullscreenEnabled() {
+ return mIsSystemOverrideToFullscreenEnabled
+ && !FALSE.equals(mBooleanPropertyAllowOrientationOverride)
+ && (mUserAspectRatio == USER_MIN_ASPECT_RATIO_UNSET
+ || mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ }
+
float getUserMinAspectRatio() {
switch (mUserAspectRatio) {
case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 5269d35..7b23004 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -28,7 +28,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.ActivityRecord.State.STOPPING;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
@@ -126,8 +125,7 @@
// The activity may be relaunched if it cannot handle the current configuration
// changes. The activity will be paused state if it is relaunched, otherwise it
// keeps the original stopped state.
- targetActivity.ensureActivityConfiguration(0 /* globalChanges */,
- false /* preserveWindow */, true /* ignoreVisibility */);
+ targetActivity.ensureActivityConfiguration(true /* ignoreVisibility */);
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Updated config=%s",
targetActivity.getConfiguration());
}
@@ -261,7 +259,7 @@
// If we updated the launch-behind state, update the visibility of the activities after
// we fetch the visible tasks to be controlled by the animation
- mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mService.mRootWindowContainer.ensureActivitiesVisible();
ActivityOptions options = null;
if (eventTime > 0) {
@@ -380,8 +378,7 @@
// transition (the target activity will be one of closing apps).
if (!controller.shouldDeferCancelWithScreenshot()
&& !targetRootTask.isFocusedRootTaskOnDisplay()) {
- targetRootTask.ensureActivitiesVisible(null /* starting */,
- 0 /* starting */, false /* preserveWindows */);
+ targetRootTask.ensureActivitiesVisible(null /* starting */);
}
// Keep target root task in place, nothing changes, so ignore the transition
// logic below
@@ -389,7 +386,7 @@
}
mWindowManager.prepareAppTransitionNone();
- mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, false);
+ mService.mRootWindowContainer.ensureActivitiesVisible();
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
// No reason to wait for the pausing activity in this case, as the hiding of
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9a75dae..6033220 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -63,7 +63,6 @@
import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.KeyguardController.KEYGUARD_SLEEP_TOKEN_TAG;
@@ -153,6 +152,7 @@
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.Slogf;
+import com.android.window.flags.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -796,6 +796,14 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ if (Flags.bundleClientTransactionFlag()) {
+ // mWmService.mResizingWindows is populated in #applySurfaceChangesTransaction()
+ handleResizingWindows();
+
+ // Called after #handleResizingWindows to include WindowStateResizeItem if any.
+ mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
+ }
+
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents();
@@ -839,12 +847,11 @@
}
}
- handleResizingWindows();
+ if (!Flags.bundleClientTransactionFlag()) {
+ handleResizingWindows();
+ }
clearFrameChangingWindows();
- // Called after #handleResizingWindows to include WindowStateResizeItem if any.
- mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
-
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
@@ -1753,8 +1760,7 @@
// activities are affecting configuration now.
// Passing null here for 'starting' param value, so that visibility of actual starting
// activity will be properly updated.
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, false /* notifyClients */);
+ ensureActivitiesVisible(null /* starting */, false /* notifyClients */);
if (displayId == INVALID_DISPLAY) {
// The caller didn't provide a valid display id, skip updating config.
@@ -1778,7 +1784,7 @@
if (displayContent != null) {
// Update the configuration of the activities on the display.
return displayContent.updateDisplayOverrideConfigurationLocked(config, starting,
- deferResume, null /* result */);
+ deferResume);
} else {
return true;
}
@@ -1865,16 +1871,18 @@
* Make sure that all activities that need to be visible in the system actually are and update
* their configuration.
*/
- void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
- boolean preserveWindows) {
- ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+ void ensureActivitiesVisible() {
+ ensureActivitiesVisible(null /* starting */);
+ }
+
+ void ensureActivitiesVisible(ActivityRecord starting) {
+ ensureActivitiesVisible(starting, true /* notifyClients */);
}
/**
- * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
+ * @see #ensureActivitiesVisible()
*/
- void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
+ void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
if (mTaskSupervisor.inActivityVisibilityUpdate()
|| mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
// Don't do recursive work.
@@ -1885,8 +1893,7 @@
// First the front root tasks. In case any are not fullscreen and are in front of home.
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
- display.ensureActivitiesVisible(starting, configChanges, preserveWindows,
- notifyClients);
+ display.ensureActivitiesVisible(starting, notifyClients);
}
} finally {
mTaskSupervisor.endActivityVisibilityUpdate();
@@ -2237,7 +2244,7 @@
try {
if (localVisibilityDeferred) {
mTaskSupervisor.setDeferRootVisibilityUpdate(false);
- ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ ensureActivitiesVisible();
}
} finally {
transitionController.continueTransitionReady();
@@ -2370,7 +2377,7 @@
// It may be nothing to resume because there are pausing activities or all the top
// activities are resumed. Then it still needs to make sure all visible activities are
// running in case the tasks were reordered or there are non-top visible activities.
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);
+ ensureActivitiesVisible();
}
}
@@ -2542,8 +2549,7 @@
// display orientation can be updated first if needed. Otherwise there may
// have redundant configuration changes due to apply outdated display
// orientation (from keyguard) to activity.
- rootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
+ rootTask.ensureActivitiesVisible(null /* starting */);
}
});
}
@@ -2885,8 +2891,7 @@
if (allowDelay) {
result[0] &= task.goToSleepIfPossible(shuttingDown);
} else {
- task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
+ task.ensureActivitiesVisible(null /* starting */);
}
});
return result[0];
@@ -3774,8 +3779,7 @@
}
}
if (!mHasActivityStarted) {
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
+ ensureActivitiesVisible();
}
return mHasActivityStarted;
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 7995028..ed54ea8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -771,6 +771,7 @@
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
+ mProcess.mWindowSession = this;
}
mAddedWindows.add(w);
}
@@ -782,6 +783,9 @@
}
}
+ boolean hasWindow() {
+ return !mAddedWindows.isEmpty();
+ }
void onWindowSurfaceVisibilityChanged(WindowSurfaceController surfaceController,
boolean visible, int type) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 671acfc..d556f09 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -34,7 +34,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
@@ -91,7 +90,6 @@
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_TASK_MSG;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -761,7 +759,7 @@
return;
}
mResizeMode = resizeMode;
- mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
mRootWindowContainer.resumeFocusedTasksTopActivities();
updateTaskDescription();
}
@@ -802,15 +800,14 @@
if (setBounds(bounds, forced) != BOUNDS_CHANGE_NONE) {
final ActivityRecord r = topRunningActivityLocked();
if (r != null) {
- kept = r.ensureActivityConfiguration(0 /* globalChanges */,
- preserveWindow);
+ kept = r.ensureActivityConfiguration();
// Preserve other windows for resizing because if resizing happens when there
// is a dialog activity in the front, the activity that still shows some
// content to the user will become black and cause flickers. Note in most cases
// this won't cause tons of irrelevant windows being preserved because only
// activities in this task may experience a bounds change. Configs for other
// activities stay the same.
- mRootWindowContainer.ensureActivitiesVisible(r, 0, preserveWindow);
+ mRootWindowContainer.ensureActivitiesVisible(r);
if (!kept) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
@@ -916,7 +913,7 @@
if (!deferResume) {
// The task might have already been running and its visibility needs to be synchronized
// with the visibility of the root task / windows.
- root.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ root.ensureActivitiesVisible();
root.resumeFocusedTasksTopActivities();
}
@@ -3509,6 +3506,8 @@
appCompatTaskInfo.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
appCompatTaskInfo.isUserFullscreenOverrideEnabled = top != null
&& top.mLetterboxUiController.shouldApplyUserFullscreenOverride();
+ appCompatTaskInfo.isSystemFullscreenOverrideEnabled = top != null
+ && top.mLetterboxUiController.isSystemOverrideToFullscreenEnabled();
appCompatTaskInfo.isFromLetterboxDoubleTap = top != null
&& top.mLetterboxUiController.isFromDoubleTap();
if (appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
@@ -4753,7 +4752,7 @@
}
if (!mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
- mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
}
@@ -4794,8 +4793,7 @@
mRootWindowContainer.resumeFocusedTasksTopActivities();
// Update visibility of activities before notifying WM. This way it won't try to resize
// windows that are no longer visible.
- mRootWindowContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible();
}
final boolean isOnHomeDisplay() {
@@ -4939,41 +4937,27 @@
* @param starting The top most activity in the task.
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link EnsureActivitiesVisibleHelper}.
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen as part of
- * {@link EnsureActivitiesVisibleHelper}.
- *
*/
- void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
- boolean preserveWindows) {
- ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+ void ensureActivitiesVisible(@Nullable ActivityRecord starting) {
+ ensureActivitiesVisible(starting, true /* notifyClients */);
}
/**
* Ensure visibility with an option to also update the configuration of visible activities.
- * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
- * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+ * @see #ensureActivitiesVisible(ActivityRecord)
+ * @see RootWindowContainer#ensureActivitiesVisible()
* @param starting The top most activity in the task.
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param notifyClients Flag indicating whether the visibility updates should be sent to the
* clients in {@link EnsureActivitiesVisibleHelper}.
- * @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link EnsureActivitiesVisibleHelper}.
- * @param configChanges Parts of the configuration that changed for this activity for evaluating
- * if the screen should be frozen as part of
- * {@link EnsureActivitiesVisibleHelper}.
*/
// TODO: Should be re-worked based on the fact that each task as a root task in most cases.
- void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
+ void ensureActivitiesVisible(@Nullable ActivityRecord starting, boolean notifyClients) {
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
forAllLeafTasks(task -> {
- task.updateActivityVisibilities(starting, configChanges, preserveWindows,
- notifyClients);
+ task.updateActivityVisibilities(starting, notifyClients);
}, true /* traverseTopToBottom */);
if (mTranslucentActivityWaiting != null &&
@@ -5274,7 +5258,7 @@
// tell WindowManager that r is visible even though it is at the back of the root
// task.
r.setVisibility(true);
- ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ ensureActivitiesVisible(null /* starting */);
// If launching behind, the app will start regardless of what's above it, so mark it
// as unknown even before prior `pause`. This also prevents a race between set-ready
// and activityPause. Launch-behind is basically only used for dream now.
@@ -5917,22 +5901,6 @@
return activities;
}
- ActivityRecord restartPackage(String packageName) {
- ActivityRecord starting = topRunningActivity();
-
- // All activities that came from the package must be
- // restarted as if there was a config change.
- forAllActivities(r -> {
- if (!r.info.packageName.equals(packageName)) return;
- r.forceNewConfig = true;
- if (starting != null && r == starting && r.isVisibleRequested()) {
- r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
- }
- });
-
- return starting;
- }
-
Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
toTop, null /*activity*/, null /*source*/, null /*options*/);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c57983c..90a3b253 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1777,13 +1777,11 @@
void onRootTaskOrderChanged(Task rootTask);
}
- void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
+ void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
try {
forAllRootTasks(rootTask -> {
- rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
- notifyClients);
+ rootTask.ensureActivitiesVisible(starting, notifyClients);
});
} finally {
mAtmService.mTaskSupervisor.endActivityVisibilityUpdate();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 5d01912..d425bdf 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -57,7 +57,6 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
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.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
@@ -950,8 +949,7 @@
}
if (shouldSleep) {
- updateActivityVisibilities(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS, true /* notifyClients */);
+ updateActivityVisibilities(null /* starting */, true /* notifyClients */);
}
return shouldSleep;
@@ -1218,12 +1216,11 @@
return top != null && top.mLaunchTaskBehind;
}
- final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,
- boolean preserveWindows, boolean notifyClients) {
+ final void updateActivityVisibilities(@Nullable ActivityRecord starting,
+ boolean notifyClients) {
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
- mEnsureActivitiesVisibleHelper.process(
- starting, configChanges, preserveWindows, notifyClients);
+ mEnsureActivitiesVisibleHelper.process(starting, notifyClients);
} finally {
mTaskSupervisor.endActivityVisibilityUpdate();
}
@@ -1249,8 +1246,7 @@
if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.allResumedActivitiesComplete()) {
// Ensure the visibility gets updated before execute app transition.
- taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, true /* notifyClients */);
+ taskDisplayArea.ensureActivitiesVisible(null /* starting */, true /* notifyClients */);
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
executeAppTransition(options);
@@ -1907,7 +1903,7 @@
prev.resumeKeyDispatchingLocked();
}
- mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+ mRootWindowContainer.ensureActivitiesVisible(resuming);
// Notify when the task stack has changed, but only if visibilities changed (not just
// focus). Also if there is an active root pinned task - we always want to notify it about
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 707f9fc..81fe453 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -35,6 +35,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.IApplicationThread;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Binder;
@@ -106,6 +107,7 @@
*/
private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
+ private final IApplicationThread mAppThread;
private final ITaskFragmentOrganizer mOrganizer;
private final int mOrganizerPid;
private final int mOrganizerUid;
@@ -169,6 +171,11 @@
TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
boolean isSystemOrganizer) {
+ if (Flags.bundleClientTransactionFlag()) {
+ mAppThread = getAppThread(pid, uid);
+ } else {
+ mAppThread = null;
+ }
mOrganizer = organizer;
mOrganizerPid = pid;
mOrganizerUid = uid;
@@ -407,7 +414,13 @@
return;
}
try {
- mOrganizer.onTransactionReady(transaction);
+ if (Flags.bundleClientTransactionFlag()) {
+ // Dispatch through IApplicationThread to ensure the binder call is in order
+ // with ClientTransaction.
+ mAppThread.scheduleTaskFragmentTransaction(mOrganizer, transaction);
+ } else {
+ mOrganizer.onTransactionReady(transaction);
+ }
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
return;
@@ -464,11 +477,6 @@
: null;
}
- @VisibleForTesting
- void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
- registerOrganizerInternal(organizer, false /* isSystemOrganizer */);
- }
-
@Override
public void registerOrganizer(
@NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
@@ -477,8 +485,7 @@
Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer);
}
- @VisibleForTesting
- void registerOrganizerInternal(
+ private void registerOrganizerInternal(
@NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
if (isSystemOrganizer) {
enforceTaskPermission("registerSystemOrganizer()");
@@ -1198,6 +1205,20 @@
}
}
+ @VisibleForTesting
+ @NonNull
+ IApplicationThread getAppThread(int pid, int uid) {
+ final WindowProcessController wpc = mAtmService.mProcessMap.getProcess(pid);
+ final IApplicationThread appThread = wpc != null && wpc.mUid == uid
+ ? wpc.getThread()
+ : null;
+ if (appThread == null) {
+ throw new IllegalArgumentException("Cannot find process for pid=" + pid
+ + " uid=" + uid);
+ }
+ return appThread;
+ }
+
/**
* Trims the given Intent to only those that are needed to for embedding rules. This helps to
* make it safer for cross-uid embedding even if we only send the Intent for trusted embedding.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f020bfa..b12855e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -43,6 +43,7 @@
import static android.view.WindowManager.TransitionType;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
+import static android.window.TransitionInfo.FLAGS_IS_OCCLUDED_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
@@ -1135,8 +1136,7 @@
// The transient hide tasks could be occluded now, e.g. returning to home. So trigger
// the update to make the activities in the tasks invisible-requested, then the next
// step can continue to commit the visibility.
- mController.mAtm.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */, true /* preserveWindows */);
+ mController.mAtm.mRootWindowContainer.ensureActivitiesVisible();
// Record all the now-hiding activities so that they are committed. Just use
// mParticipants because we can avoid a new list this way.
for (int i = 0; i < mTransientHideTasks.size(); ++i) {
@@ -2862,8 +2862,7 @@
* check whether to deliver the new configuration to clients.
*/
@Nullable
- ArrayList<ActivityRecord> applyDisplayChangeIfNeeded() {
- ArrayList<ActivityRecord> activitiesMayChange = null;
+ void applyDisplayChangeIfNeeded(@NonNull ArraySet<WindowContainer<?>> activitiesMayChange) {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = mParticipants.valueAt(i);
final DisplayContent dc = wc.asDisplayContent();
@@ -2880,18 +2879,13 @@
// If the update is deferred, sendNewConfiguration won't deliver new configuration to
// clients, then it is the caller's responsibility to deliver the changes.
if (mController.mAtm.mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
- if (activitiesMayChange == null) {
- activitiesMayChange = new ArrayList<>();
- }
- final ArrayList<ActivityRecord> visibleActivities = activitiesMayChange;
dc.forAllActivities(r -> {
if (r.isVisibleRequested()) {
- visibleActivities.add(r);
+ activitiesMayChange.add(r);
}
});
}
}
- return activitiesMayChange;
}
boolean getLegacyIsReady() {
@@ -3067,6 +3061,10 @@
Slog.e(TAG, "Unexpected launch-task-behind operation in shell transition");
flags |= FLAG_TASK_LAUNCHING_BEHIND;
}
+ if ((topActivity.mTransitionChangeFlags & FLAGS_IS_OCCLUDED_NO_ANIMATION)
+ == FLAGS_IS_OCCLUDED_NO_ANIMATION) {
+ flags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
+ }
}
if (task.voiceSession != null) {
flags |= FLAG_IS_VOICE_INTERACTION;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 750fd50..c4e1d6e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -148,8 +148,7 @@
dc.checkAppWindowsReadyToShow();
if (accessibilityController.hasCallbacks()) {
- accessibilityController.drawMagnifiedRegionBorderIfNeeded(dc.mDisplayId,
- mTransaction);
+ accessibilityController.drawMagnifiedRegionBorderIfNeeded(dc.mDisplayId);
}
if (dc.isAnimating(animationFlags, ANIMATION_TYPE_ALL)) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index 89a70e5..7b0d931 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -43,8 +43,6 @@
/* Start Available Flags */
- final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag();
-
final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync();
final boolean mAllowsScreenSizeDecoupledFromStatusBarAndCutout =
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c1310a6..502912a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3105,7 +3105,7 @@
try {
synchronized (mGlobalLock) {
if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
- mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ mRoot.ensureActivitiesVisible();
}
}
} finally {
@@ -5748,7 +5748,6 @@
case INSETS_CHANGED: {
synchronized (mGlobalLock) {
if (mWindowsInsetsChanged > 0) {
- mWindowsInsetsChanged = 0;
// We need to update resizing windows and dispatch the new insets state
// to them.
mWindowPlacerLocked.performSurfacePlacement();
@@ -6848,6 +6847,7 @@
pw.println(defaultDisplayContent.getLastOrientation());
pw.print(" mWaitingForConfig=");
pw.println(defaultDisplayContent.mWaitingForConfig);
+ pw.print(" mWindowsInsetsChanged="); pw.println(mWindowsInsetsChanged);
mRotationWatcherController.dump(pw);
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4b99432..9e4a31c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -65,7 +65,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
@@ -571,14 +570,15 @@
mService.deferWindowLayout();
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
- final ArrayList<ActivityRecord> activitiesMayChange =
- transition != null ? transition.applyDisplayChangeIfNeeded() : null;
- if (activitiesMayChange != null) {
- effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+ final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
+ if (transition != null) {
+ transition.applyDisplayChangeIfNeeded(haveConfigChanges);
+ if (!haveConfigChanges.isEmpty()) {
+ effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+ }
}
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
- final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
t.getChanges().entrySet().iterator();
while (entries.hasNext()) {
@@ -626,7 +626,7 @@
// When removing pip, make sure that onStop is sent to the app ahead of
// onPictureInPictureModeChanged.
// See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss
- wc.asTask().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ wc.asTask().ensureActivitiesVisible(null /* starting */);
wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities(
null /* launchedActivity */, false /* processPausingActivities */,
"force-stop-on-removing-pip");
@@ -692,29 +692,16 @@
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
// Already calls ensureActivityConfig
- mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mService.mRootWindowContainer.ensureActivitiesVisible();
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
} else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
haveConfigChanges.valueAt(i).forAllActivities(r -> {
- r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);
- if (activitiesMayChange != null) {
- activitiesMayChange.remove(r);
+ if (r.isVisibleRequested()) {
+ r.ensureActivityConfiguration(true /* ignoreVisibility */);
}
});
}
- // TODO(b/258618073): Combine with haveConfigChanges after confirming that there
- // is no problem to always preserve window. Currently this uses the parameters
- // as ATMS#ensureConfigAndVisibilityAfterUpdate.
- if (activitiesMayChange != null) {
- for (int i = activitiesMayChange.size() - 1; i >= 0; --i) {
- final ActivityRecord ar = activitiesMayChange.get(i);
- if (!ar.isVisibleRequested()) continue;
- ar.ensureActivityConfiguration(0 /* globalChanges */,
- !PRESERVE_WINDOWS, true /* ignoreVisibility */,
- false /* isRequestedOrientationChanged */);
- }
- }
}
if (effects != 0) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 5721750..b8fa5e5 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -188,6 +188,10 @@
// Set to true when process was launched with a wrapper attached
private volatile boolean mUsingWrapper;
+ /** Non-null if this process may have a window. */
+ @Nullable
+ Session mWindowSession;
+
// Thread currently set for VR scheduling
int mVrThreadTid;
@@ -399,7 +403,7 @@
// the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate).
try {
// No WM lock here.
- mAtm.getLifecycleManager().scheduleTransactionItemUnlocked(
+ mAtm.getLifecycleManager().scheduleTransactionItemNow(
thread, configurationChangeItem);
} catch (Exception e) {
Slog.e(TAG_CONFIGURATION, "Failed to schedule ConfigurationChangeItem="
@@ -989,7 +993,7 @@
if (packageName.equals(r.packageName)
&& r.applyAppSpecificConfig(nightMode, localesOverride, gender)
&& r.isVisibleRequested()) {
- r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */);
+ r.ensureActivityConfiguration();
}
}
}
@@ -1675,7 +1679,12 @@
private void scheduleClientTransactionItem(@NonNull IApplicationThread thread,
@NonNull ClientTransactionItem transactionItem) {
try {
- mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem);
+ if (mWindowSession != null && mWindowSession.hasWindow()) {
+ mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem);
+ } else {
+ // Non-UI process can handle the change directly.
+ mAtm.getLifecycleManager().scheduleTransactionItemNow(thread, transactionItem);
+ }
} catch (DeadObjectException e) {
// Expected if the process has been killed.
Slog.w(TAG_CONFIGURATION, "Failed for dead process. ClientTransactionItem="
@@ -1723,7 +1732,7 @@
overrideConfig.assetsSeq = assetSeq;
r.onRequestedOverrideConfigurationChanged(overrideConfig);
if (r.isVisibleRequested()) {
- r.ensureActivityConfiguration(0, true);
+ r.ensureActivityConfiguration();
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 56e7c69..315c00f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -52,7 +52,6 @@
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
@@ -203,7 +202,6 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.PowerManager;
-import android.os.PowerManager.WakeReason;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -254,6 +252,7 @@
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.RefreshRatePolicy.FrameRateVote;
import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
@@ -703,11 +702,6 @@
*/
private final Region mTapExcludeRegion = new Region();
- /**
- * Used for testing because the real PowerManager is final.
- */
- private PowerManagerWrapper mPowerManagerWrapper;
-
private static final StringBuilder sTmpSB = new StringBuilder();
/**
@@ -1062,34 +1056,9 @@
return mOnBackInvokedCallbackInfo;
}
- interface PowerManagerWrapper {
- void wakeUp(long time, @WakeReason int reason, String details);
-
- boolean isInteractive();
-
- }
-
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
- this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId,
- ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
- @Override
- public void wakeUp(long time, @WakeReason int reason, String details) {
- service.mPowerManager.wakeUp(time, reason, details);
- }
-
- @Override
- public boolean isInteractive() {
- return service.mPowerManager.isInteractive();
- }
- });
- }
-
- WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
- int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
- PowerManagerWrapper powerManagerWrapper) {
super(service);
mTmpTransaction = service.mTransactionFactory.get();
mSession = s;
@@ -1107,7 +1076,6 @@
mViewVisibility = viewVisibility;
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
- mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
mActivityRecord != null
@@ -1247,13 +1215,14 @@
* @see ActivityRecord#hasSizeCompatBounds()
*/
boolean hasCompatScale() {
- if ((mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0) {
- return true;
- }
if (mAttrs.type == TYPE_APPLICATION_STARTING) {
// Exclude starting window because it is not displayed by the application.
return false;
}
+ if (mWmService.mAtmService.mCompatModePackages.useLegacyScreenCompatMode(
+ mSession.mProcess.mInfo.packageName)) {
+ return true;
+ }
return mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
|| mOverrideScale != 1f;
}
@@ -1474,16 +1443,7 @@
this, mWindowFrames.getInsetsChangedInfo(),
configChanged, didFrameInsetsChange);
- if (insetsChanged) {
- mWindowFrames.setInsetsChanged(false);
- if (mWmService.mWindowsInsetsChanged > 0) {
- mWmService.mWindowsInsetsChanged--;
- }
- if (mWmService.mWindowsInsetsChanged == 0) {
- mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
- }
- }
-
+ consumeInsetsChange();
onResizeHandled();
mWmService.makeWindowFreezingScreenIfNeededLocked(this);
@@ -2380,6 +2340,8 @@
mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens(
getWindowToken());
+
+ consumeInsetsChange();
}
@Override
@@ -2831,12 +2793,12 @@
boolean canTurnScreenOn = mActivityRecord == null || mActivityRecord.currentLaunchCanTurnScreenOn();
if (allowTheaterMode && canTurnScreenOn
- && (mWmService.mAtmService.isDreaming()
- || !mPowerManagerWrapper.isInteractive())) {
+ && (mWmService.mAtmService.isDreaming()
+ || !mWmService.mPowerManager.isInteractive())) {
if (DEBUG_VISIBILITY || DEBUG_POWER) {
Slog.v(TAG, "Relayout window turning screen on: " + this);
}
- mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
+ mWmService.mPowerManager.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
}
@@ -3707,7 +3669,7 @@
markRedrawForSyncReported();
- if (mWmService.mFlags.mWindowStateResizeItemFlag) {
+ if (Flags.bundleClientTransactionFlag()) {
getProcess().scheduleClientTransactionItem(
WindowStateResizeItem.obtain(mClient, mClientWindowFrames, reportDraw,
mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,
@@ -3753,6 +3715,16 @@
return mClient instanceof IWindow.Stub;
}
+ private void consumeInsetsChange() {
+ if (mWindowFrames.hasInsetsChanged()) {
+ mWindowFrames.setInsetsChanged(false);
+ mWmService.mWindowsInsetsChanged--;
+ if (mWmService.mWindowsInsetsChanged == 0) {
+ mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
+ }
+ }
+ }
+
/**
* Called when the insets state changed.
*/
@@ -3760,10 +3732,10 @@
ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsChanged for %s ", this);
if (!mWindowFrames.hasInsetsChanged()) {
mWindowFrames.setInsetsChanged(true);
+ mWmService.mWindowsInsetsChanged++;
// If the new InsetsState won't be dispatched before releasing WM lock, the following
// message will be executed.
- mWmService.mWindowsInsetsChanged++;
mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED);
}
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 061fe0f..cc08488 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -31,5 +31,5 @@
per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
-# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java
-per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS
+# Bug component : 158088 = per-file *AnrTimer*
+per-file *AnrTimer* = file:/PERFORMANCE_OWNERS
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0dd0564..9ba0a2a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -358,8 +358,8 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override;
bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override;
void interceptKeyBeforeQueueing(const KeyEvent& keyEvent, uint32_t& policyFlags) override;
- void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
- uint32_t& policyFlags) override;
+ void interceptMotionBeforeQueueing(int32_t displayId, uint32_t source, int32_t action,
+ nsecs_t when, uint32_t& policyFlags) override;
nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
uint32_t policyFlags) override;
std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent& keyEvent,
@@ -1496,7 +1496,8 @@
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}
-void NativeInputManager::interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
+void NativeInputManager::interceptMotionBeforeQueueing(int32_t displayId, uint32_t source,
+ int32_t action, nsecs_t when,
uint32_t& policyFlags) {
ATRACE_CALL();
// Policy:
@@ -1525,7 +1526,7 @@
const jint wmActions =
env->CallIntMethod(mServiceObj,
gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,
- displayId, when, policyFlags);
+ displayId, source, action, when, policyFlags);
if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingNonInteractive")) {
return;
}
@@ -2943,7 +2944,7 @@
"interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I");
GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz,
- "interceptMotionBeforeQueueingNonInteractive", "(IJI)I");
+ "interceptMotionBeforeQueueingNonInteractive", "(IIIJI)I");
GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
"interceptKeyBeforeDispatching",
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 c625b1e..3cbceec 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -595,7 +595,7 @@
<!-- Sets the brightness mapping of the desired screen brightness to the corresponding
lux for the current display -->
<xs:element name="luxToBrightnessMapping" type="luxToBrightnessMapping"
- minOccurs="0" maxOccurs="1">
+ minOccurs="0" maxOccurs="unbounded">
<xs:annotation name="final"/>
</xs:element>
</xs:sequence>
@@ -619,12 +619,20 @@
This is used in place of config_autoBrightnessLevels and config_autoBrightnessLcdBacklightValues
defined in the config XML resource.
+
+ On devices that allow users to choose from a set of predefined options in display
+ auto-brightness settings, multiple mappings for different modes and settings can be defined.
+
+ If no mode is specified, the mapping will be used for the default mode.
+ If no setting is specified, the mapping will be used for the normal brightness setting.
-->
<xs:complexType name="luxToBrightnessMapping">
<xs:element name="map" type="nonNegativeFloatToFloatMap">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
+ <xs:element name="mode" type="AutoBrightnessModeName" minOccurs="0"/>
+ <xs:element name="setting" type="AutoBrightnessSettingName" minOccurs="0"/>
</xs:complexType>
<!-- Represents a point in the display brightness mapping, representing the lux level from the
@@ -757,4 +765,23 @@
</xs:element>
</xs:sequence>
</xs:complexType>
+
+ <!-- Predefined type names as defined by
+ AutomaticBrightnessController.AutomaticBrightnessMode -->
+ <xs:simpleType name="AutoBrightnessModeName">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="idle"/>
+ <xs:enumeration value="doze"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- Predefined auto-brighntess settings -->
+ <xs:simpleType name="AutoBrightnessSettingName">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="dim"/>
+ <xs:enumeration value="normal"/>
+ <xs:enumeration value="bright"/>
+ </xs:restriction>
+ </xs:simpleType>
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 8c8c123..79ea274 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -8,13 +8,26 @@
method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
method public boolean getEnabled();
- method public final com.android.server.display.config.LuxToBrightnessMapping getLuxToBrightnessMapping();
+ method public final java.util.List<com.android.server.display.config.LuxToBrightnessMapping> getLuxToBrightnessMapping();
method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
method public void setEnabled(boolean);
- method public final void setLuxToBrightnessMapping(com.android.server.display.config.LuxToBrightnessMapping);
+ }
+
+ public enum AutoBrightnessModeName {
+ method public String getRawName();
+ enum_constant public static final com.android.server.display.config.AutoBrightnessModeName _default;
+ enum_constant public static final com.android.server.display.config.AutoBrightnessModeName doze;
+ enum_constant public static final com.android.server.display.config.AutoBrightnessModeName idle;
+ }
+
+ public enum AutoBrightnessSettingName {
+ method public String getRawName();
+ enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName bright;
+ enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName dim;
+ enum_constant public static final com.android.server.display.config.AutoBrightnessSettingName normal;
}
public class BlockingZoneConfig {
@@ -220,7 +233,11 @@
public class LuxToBrightnessMapping {
ctor public LuxToBrightnessMapping();
method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap();
+ method public com.android.server.display.config.AutoBrightnessModeName getMode();
+ method public com.android.server.display.config.AutoBrightnessSettingName getSetting();
method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+ method public void setMode(com.android.server.display.config.AutoBrightnessModeName);
+ method public void setSetting(com.android.server.display.config.AutoBrightnessSettingName);
}
public class NitsMap {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 4a2e1cb..686b2a8 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -65,7 +65,6 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
import com.android.server.infra.AbstractMasterSystemService;
@@ -1204,6 +1203,9 @@
// If the app being removed matches any of the package names from
// this list then don't add it in the output.
Set<String> providers = new HashSet<>();
+ if (rawProviders == null || packageName == null) {
+ return providers;
+ }
for (String rawComponentName : rawProviders.split(":")) {
if (TextUtils.isEmpty(rawComponentName)
|| rawComponentName.equals("null")) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 59e95e7..1185a4e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2734,11 +2734,7 @@
// AdServicesManagerService (PP API service)
t.traceBegin("StartAdServicesManagerService");
- SystemService adServices = mSystemServiceManager
- .startService(AD_SERVICES_MANAGER_SERVICE_CLASS);
- if (adServices instanceof Dumpable) {
- mDumper.addDumpable((Dumpable) adServices);
- }
+ mSystemServiceManager.startService(AD_SERVICES_MANAGER_SERVICE_CLASS);
t.traceEnd();
// OnDevicePersonalizationSystemService
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 f69f628..022268d 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
@@ -1262,7 +1262,7 @@
val apexModuleName = packageState.apexModuleName
val packageName = packageState.packageName
return when {
- packageState.isVendor ->
+ packageState.isVendor || packageState.isOdm ->
permissionAllowlist.getVendorPrivilegedAppAllowlistState(
packageName,
permissionName
@@ -1471,12 +1471,15 @@
// In any case, don't grant a privileged permission to privileged vendor apps,
// if the permission's protectionLevel does not have the extra vendorPrivileged
// flag.
- if (packageState.isVendor && !permission.isVendorPrivileged) {
+ if (
+ (packageState.isVendor || packageState.isOdm) &&
+ !permission.isVendorPrivileged
+ ) {
Slog.w(
LOG_TAG,
"Permission $permissionName cannot be granted to privileged" +
- " vendor app $packageName because it isn't a vendorPrivileged" +
- " permission"
+ " vendor (or odm) app $packageName because it isn't a" +
+ " vendorPrivileged permission"
)
return false
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index 03e45a2..71f5c75 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -58,6 +58,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.permission.CompatibilityPermissionInfo;
@@ -84,7 +86,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.PackageParserUtils;
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
@@ -185,7 +187,7 @@
@Test
public void test_serializePackage() throws Exception {
- try (PackageParser2 pp = PackageParser2.forParsingFileWithDefaults()) {
+ try (PackageParser2 pp = PackageParserUtils.forParsingFileWithDefaults()) {
AndroidPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
true /* useCaches */).hideAsFinal();
@@ -363,7 +365,7 @@
actualDisplayCategory = activity.getRequiredDisplayCategory();
}
}
- } catch (PackageManagerException e) {
+ } catch (PackageParserException e) {
assertThat(e.getMessage()).contains(
"requiredDisplayCategory attribute can only consist"
+ " of alphanumeric characters, '_', and '.'");
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
index 8a74e24..3761240 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -21,8 +21,8 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.TestPackageParser2;
import junit.framework.Assert;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index b63950c..a28b28a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -38,6 +38,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivityUtils;
import com.android.internal.pm.pkg.component.ParsedComponent;
@@ -45,7 +46,6 @@
import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.pm.pkg.component.ParsedPermissionUtils;
import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.PackageManagerException;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.test.service.server.R;
@@ -608,7 +608,7 @@
try {
parsePackage(filename, resId, x -> x);
expect.withMessage("Expected parsing error %s from %s", result, filename).fail();
- } catch (PackageManagerException expected) {
+ } catch (PackageParserException expected) {
expect.that(expected.error).isEqualTo(result);
}
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 98af63c..1d668cd 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -18,8 +18,8 @@
import android.content.pm.PackageManager
import android.platform.test.annotations.Postsubmit
+import com.android.internal.pm.parsing.PackageParserException
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
-import com.android.server.pm.PackageManagerException
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageManagerServiceUtils
import java.io.File
@@ -39,7 +39,7 @@
@Postsubmit
class SystemPartitionParseTest {
- private val parser = PackageParser2.forParsingFileWithDefaults()
+ private val parser = PackageParserUtils.forParsingFileWithDefaults()
@get:Rule
val tempFolder = TemporaryFolder()
@@ -86,7 +86,7 @@
}
}
.mapNotNull { it.exceptionOrNull() }
- .filterNot { (it as? PackageManagerException)?.error ==
+ .filterNot { (it as? PackageParserException)?.error ==
PackageManager.INSTALL_PARSE_FAILED_SKIPPED }
if (exceptions.isEmpty()) return
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 189d9bb..fb73aff 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import static org.junit.Assert.assertEquals;
@@ -29,21 +30,22 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
import android.os.PowerManager;
+import android.provider.Settings;
+import android.testing.TestableContext;
import android.util.MathUtils;
import android.util.Spline;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
-
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import java.util.Arrays;
@@ -107,20 +109,6 @@
468.5f,
};
- private static final int[] DISPLAY_LEVELS_INT = {
- 9,
- 30,
- 45,
- 62,
- 78,
- 96,
- 119,
- 146,
- 178,
- 221,
- 255
- };
-
private static final float[] DISPLAY_LEVELS = {
0.03f,
0.11f,
@@ -168,66 +156,35 @@
private static final float TOLERANCE = 0.0001f;
- @Mock
- DisplayWhiteBalanceController mMockDwbc;
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Before
+ public void setUp() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL);
+ }
@Test
- public void testSimpleStrategyMappingAtControlPoints_IntConfig() {
- Resources res = createResources(DISPLAY_LEVELS_INT);
- DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ public void testSimpleStrategyMappingAtControlPoints() {
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 0; i < LUX_LEVELS.length; i++) {
- final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1,
- PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_INT[i]);
- assertEquals(expectedLevel,
- simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/);
+ assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]), TOLERANCE);
}
}
@Test
- public void testSimpleStrategyMappingBetweenControlPoints_IntConfig() {
- Resources res = createResources(DISPLAY_LEVELS_INT);
- DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
- assertNotNull("BrightnessMappingStrategy should not be null", simple);
- for (int i = 1; i < LUX_LEVELS.length; i++) {
- final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
- final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
- assertTrue("Desired brightness should be between adjacent control points.",
- backlight > DISPLAY_LEVELS_INT[i - 1]
- && backlight < DISPLAY_LEVELS_INT[i]);
- }
- }
-
- @Test
- public void testSimpleStrategyMappingAtControlPoints_FloatConfig() {
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
- EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
- assertNotNull("BrightnessMappingStrategy should not be null", simple);
- for (int i = 0; i < LUX_LEVELS.length; i++) {
- assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]),
- /* tolerance= */ 0.0001f);
- }
- }
-
- @Test
- public void testSimpleStrategyMappingBetweenControlPoints_FloatConfig() {
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
- EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ public void testSimpleStrategyMappingBetweenControlPoints() {
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 1; i < LUX_LEVELS.length; i++) {
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
@@ -239,10 +196,10 @@
@Test
public void testSimpleStrategyIgnoresNewConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_INT);
- DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
final float[] lux = { 0f, 1f };
final float[] nits = { 0, PowerManager.BRIGHTNESS_ON };
@@ -255,27 +212,25 @@
@Test
public void testSimpleStrategyIgnoresNullConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_INT);
- DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
strategy.setBrightnessConfiguration(null);
- final int n = DISPLAY_LEVELS_INT.length;
- final float expectedBrightness =
- (float) DISPLAY_LEVELS_INT[n - 1] / PowerManager.BRIGHTNESS_ON;
+ final int n = DISPLAY_LEVELS.length;
+ final float expectedBrightness = DISPLAY_LEVELS[n - 1];
assertEquals(expectedBrightness,
strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@Test
public void testPhysicalStrategyMappingAtControlPoints() {
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT,
- LUX_LEVELS, DISPLAY_LEVELS_NITS);
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
+ .build();
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
for (int i = 0; i < LUX_LEVELS.length; i++) {
final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1],
@@ -290,11 +245,11 @@
@Test
public void testPhysicalStrategyMappingBetweenControlPoints() {
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE,
- LUX_LEVELS, DISPLAY_LEVELS_NITS);
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
+ .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
Spline brightnessToNits =
Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS);
@@ -309,11 +264,11 @@
@Test
public void testPhysicalStrategyUsesNewConfigurations() {
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
+ .build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
final float[] lux = {0f, 1f};
final float[] nits = {
@@ -336,11 +291,11 @@
@Test
public void testPhysicalStrategyRecalculateSplines() {
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
+ .build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
@@ -381,11 +336,12 @@
@Test
public void testDefaultStrategyIsPhysical() {
- Resources res = createResources(DISPLAY_LEVELS_INT);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevels(DISPLAY_LEVELS)
+ .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
+ .build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
}
@@ -396,19 +352,19 @@
float tmp = lux[idx];
lux[idx] = lux[idx + 1];
lux[idx + 1] = tmp;
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
+ .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNull(strategy);
// And make sure we get the same result even if it's monotone but not increasing.
lux[idx] = lux[idx + 1];
- ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux,
- DISPLAY_LEVELS_NITS);
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
+ .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
}
@@ -419,82 +375,74 @@
// Make sure it's strictly increasing so that the only failure is the differing array
// lengths
lux[lux.length - 1] = lux[lux.length - 2] + 1;
- Resources res = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
+ .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNull(strategy);
- res = createResources(DISPLAY_LEVELS_INT);
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ ddc = new DdcBuilder().setAutoBrightnessLevelsLux(lux)
+ .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS)
+ .setAutoBrightnessLevels(DISPLAY_LEVELS).build();
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
// Extra backlight level
- final int[] backlight = Arrays.copyOf(
- DISPLAY_LEVELS_INT, DISPLAY_LEVELS_INT.length + 1);
+ final float[] backlight = Arrays.copyOf(DISPLAY_LEVELS, DISPLAY_LEVELS.length + 1);
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
- res = createResources(backlight);
- ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, EMPTY_FLOAT_ARRAY);
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ setUpResources();
+ ddc = new DdcBuilder().setAutoBrightnessLevels(backlight).build();
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
// Extra nits level
final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length + 1);
nits[nits.length - 1] = nits[nits.length - 2] + 1;
- res = createResources(EMPTY_INT_ARRAY);
- ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, nits);
- strategy = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ setUpResources();
+ ddc = new DdcBuilder().setAutoBrightnessLevelsNits(nits)
+ .setAutoBrightnessLevels(EMPTY_FLOAT_ARRAY).build();
+ strategy = BrightnessMappingStrategy.create(mContext, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* displayWhiteBalanceController= */ null);
assertNull(strategy);
}
@Test
public void testPhysicalStrategyRequiresNitsMapping() {
- Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
- DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/);
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
- assertNull(physical);
-
- res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
- physical = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
- assertNull(physical);
-
- res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
- physical = BrightnessMappingStrategy.create(res, ddc, AUTO_BRIGHTNESS_MODE_DEFAULT,
- mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setNitsRange(EMPTY_FLOAT_ARRAY).build();
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertNull(physical);
}
@Test
public void testStrategiesAdaptToUserDataPoint() {
- Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE,
- LUX_LEVELS, DISPLAY_LEVELS_NITS);
- assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc));
- ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- res = createResources(DISPLAY_LEVELS_INT);
- assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc));
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
+ .setAutoBrightnessLevelsNits(DISPLAY_LEVELS_NITS).build();
+ assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null));
+ ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
+ .setAutoBrightnessLevels(DISPLAY_LEVELS).build();
+ setUpResources();
+ assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null));
}
@Test
public void testIdleModeConfigLoadsCorrectly() {
- Resources res = createResourcesIdle(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
+ setUpResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
+ DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
+ .build();
// Create an idle mode bms
// This will fail if it tries to fetch the wrong configuration.
- BrightnessMappingStrategy bms = BrightnessMappingStrategy.create(res, ddc,
+ BrightnessMappingStrategy bms = BrightnessMappingStrategy.create(mContext, ddc,
AUTO_BRIGHTNESS_MODE_IDLE,
- mMockDwbc);
+ /* displayWhiteBalanceController= */ null);
assertNotNull("BrightnessMappingStrategy should not be null", bms);
// Ensure that the config is the one we set
@@ -562,81 +510,31 @@
assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/);
}
- private Resources createResources(int[] brightnessLevelsBacklight) {
- return createResources(brightnessLevelsBacklight, EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY);
+ private void setUpResources() {
+ setUpResources(EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY);
}
- private Resources createResourcesIdle(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) {
- return createResources(EMPTY_INT_ARRAY,
- luxLevelsIdle, brightnessLevelsNitsIdle);
- }
-
- private Resources createResources(int[] brightnessLevelsBacklight, int[] luxLevelsIdle,
- float[] brightnessLevelsNitsIdle) {
-
- Resources mockResources = mock(Resources.class);
+ private void setUpResources(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) {
if (luxLevelsIdle.length > 0) {
int[] luxLevelsIdleResource = Arrays.copyOfRange(luxLevelsIdle, 1,
luxLevelsIdle.length);
- when(mockResources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLevelsIdle))
- .thenReturn(luxLevelsIdleResource);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_autoBrightnessLevelsIdle,
+ luxLevelsIdleResource);
}
- when(mockResources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
- .thenReturn(brightnessLevelsBacklight);
-
TypedArray mockBrightnessLevelNitsIdle = createFloatTypedArray(brightnessLevelsNitsIdle);
- when(mockResources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle))
- .thenReturn(mockBrightnessLevelNitsIdle);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle,
+ mockBrightnessLevelNitsIdle);
- when(mockResources.getInteger(
- com.android.internal.R.integer.config_screenBrightnessSettingMinimum))
- .thenReturn(1);
- when(mockResources.getInteger(
- com.android.internal.R.integer.config_screenBrightnessSettingMaximum))
- .thenReturn(255);
- when(mockResources.getFraction(
- com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1))
- .thenReturn(MAXIMUM_GAMMA);
- return mockResources;
- }
-
- private DisplayDeviceConfig createDdc() {
- return createDdc(DISPLAY_RANGE_NITS);
- }
-
- private DisplayDeviceConfig createDdc(float[] nitsArray) {
- return createDdc(nitsArray, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT);
- }
-
- private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray) {
- DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
- when(mockDdc.getNits()).thenReturn(nitsArray);
- when(mockDdc.getBrightness()).thenReturn(backlightArray);
- when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(LUX_LEVELS);
- when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
- when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(EMPTY_FLOAT_ARRAY);
- return mockDdc;
- }
-
- private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
- float[] luxLevelsFloat, float[] brightnessLevelsNits) {
- return createDdc(nitsArray, backlightArray, luxLevelsFloat, brightnessLevelsNits,
- EMPTY_FLOAT_ARRAY);
- }
-
- private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
- float[] luxLevelsFloat, float[] brightnessLevelsNits, float[] brightnessLevels) {
- DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
- when(mockDdc.getNits()).thenReturn(nitsArray);
- when(mockDdc.getBrightness()).thenReturn(backlightArray);
- when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(luxLevelsFloat);
- when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits);
- when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(brightnessLevels);
- return mockDdc;
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.integer.config_screenBrightnessSettingMinimum, 1);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.integer.config_screenBrightnessSettingMaximum, 255);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
+ MAXIMUM_GAMMA);
}
private TypedArray createFloatTypedArray(float[] vals) {
@@ -677,12 +575,11 @@
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
- Resources resources = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
- GAMMA_CORRECTION_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
+ .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
// Let's start with a validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -708,12 +605,11 @@
final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
- Resources resources = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
- GAMMA_CORRECTION_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
+ .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
// Validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -736,12 +632,11 @@
public void testGammaCorrectionExtremeChangeAtCenter() {
// Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we
// just make sure the adjustment reflects the change.
- Resources resources = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
- GAMMA_CORRECTION_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
+ .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f);
strategy.addUserDataPoint(/* lux= */ 2500, /* brightness= */ 1.0f);
assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), /* delta= */ 0.0001f);
@@ -760,12 +655,11 @@
final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0);
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4);
- Resources resources = createResources(EMPTY_INT_ARRAY);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
- DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
- GAMMA_CORRECTION_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
- AUTO_BRIGHTNESS_MODE_DEFAULT, mMockDwbc);
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder().setAutoBrightnessLevelsLux(GAMMA_CORRECTION_LUX)
+ .setAutoBrightnessLevelsNits(GAMMA_CORRECTION_NITS).build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_DEFAULT, /* displayWhiteBalanceController= */ null);
// Validity, as per tradition:
assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -790,11 +684,93 @@
@Test
public void testGetMode() {
- Resources res = createResourcesIdle(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc,
- AUTO_BRIGHTNESS_MODE_IDLE,
- mMockDwbc);
+ setUpResources(LUX_LEVELS_IDLE, DISPLAY_LEVELS_NITS_IDLE);
+ DisplayDeviceConfig ddc = new DdcBuilder().setBrightnessRange(BACKLIGHT_RANGE_ZERO_TO_ONE)
+ .build();
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(mContext, ddc,
+ AUTO_BRIGHTNESS_MODE_IDLE, /* displayWhiteBalanceController= */ null);
assertEquals(AUTO_BRIGHTNESS_MODE_IDLE, strategy.getMode());
}
+
+ @Test
+ public void testAutoBrightnessModeAndPreset() {
+ int mode = AUTO_BRIGHTNESS_MODE_DOZE;
+ int preset = Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM;
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, preset);
+
+ setUpResources();
+ DisplayDeviceConfig ddc = new DdcBuilder()
+ .setAutoBrightnessLevels(mode, preset, DISPLAY_LEVELS)
+ .setAutoBrightnessLevelsLux(mode, preset, LUX_LEVELS).build();
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(mContext, ddc, mode,
+ /* displayWhiteBalanceController= */ null);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 0; i < LUX_LEVELS.length; i++) {
+ assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]), TOLERANCE);
+ }
+ }
+
+ private static class DdcBuilder {
+ private DisplayDeviceConfig mDdc;
+
+ DdcBuilder() {
+ mDdc = mock(DisplayDeviceConfig.class);
+ when(mDdc.getNits()).thenReturn(DISPLAY_RANGE_NITS);
+ when(mDdc.getBrightness()).thenReturn(DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT);
+ when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)).thenReturn(LUX_LEVELS);
+ when(mDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
+ when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL))
+ .thenReturn(EMPTY_FLOAT_ARRAY);
+ }
+
+ DdcBuilder setNitsRange(float[] nitsArray) {
+ when(mDdc.getNits()).thenReturn(nitsArray);
+ return this;
+ }
+
+ DdcBuilder setBrightnessRange(float[] brightnessArray) {
+ when(mDdc.getBrightness()).thenReturn(brightnessArray);
+ return this;
+ }
+
+ DdcBuilder setAutoBrightnessLevelsLux(float[] luxLevels) {
+ when(mDdc.getAutoBrightnessBrighteningLevelsLux(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL)).thenReturn(luxLevels);
+ return this;
+ }
+
+ DdcBuilder setAutoBrightnessLevelsLux(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset,
+ float[] luxLevels) {
+ when(mDdc.getAutoBrightnessBrighteningLevelsLux(mode, preset)).thenReturn(luxLevels);
+ return this;
+ }
+
+ DdcBuilder setAutoBrightnessLevelsNits(float[] brightnessLevelsNits) {
+ when(mDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits);
+ return this;
+ }
+
+ DdcBuilder setAutoBrightnessLevels(float[] brightnessLevels) {
+ when(mDdc.getAutoBrightnessBrighteningLevels(AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL))
+ .thenReturn(brightnessLevels);
+ return this;
+ }
+
+ DdcBuilder setAutoBrightnessLevels(
+ @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset,
+ float[] brightnessLevels) {
+ when(mDdc.getAutoBrightnessBrighteningLevels(mode, preset))
+ .thenReturn(brightnessLevels);
+ return this;
+ }
+
+ DisplayDeviceConfig build() {
+ return mDdc;
+ }
+ }
}
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 31d7e88..7a84406 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -17,6 +17,9 @@
package com.android.server.display;
+import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.config.SensorData.SupportedMode;
import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -39,6 +42,7 @@
import android.content.res.TypedArray;
import android.hardware.display.DisplayManagerInternal;
import android.os.Temperature;
+import android.provider.Settings;
import android.util.SparseArray;
import android.util.Spline;
import android.view.SurfaceControl;
@@ -47,7 +51,6 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
-import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -605,10 +608,15 @@
private void verifyConfigValuesFromConfigResource() {
assertNull(mDisplayDeviceConfig.getName());
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
- float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
+ assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(),
+ new float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA);
+ assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DEFAULT, Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL),
+ new float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
+ assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DEFAULT, Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL),
+ new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100),
+ brightnessIntToFloat(150)}, SMALL_DELTA);
// Test thresholds
assertEquals(0, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), ZERO_DELTA);
@@ -674,7 +682,7 @@
assertEquals("test_light_sensor", mDisplayDeviceConfig.getAmbientLightSensor().type);
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
- assertEquals(BrightnessSynchronizer.brightnessIntToFloat(35),
+ assertEquals(brightnessIntToFloat(35),
mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
}
@@ -734,9 +742,40 @@
getValidProxSensor(), /* includeIdleMode= */ false));
assertArrayEquals(new float[]{0.0f, 80},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA);
assertArrayEquals(new float[]{0.2f, 0.3f},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(), SMALL_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA);
+
+ assertArrayEquals(new float[]{0.0f, 90},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), ZERO_DELTA);
+ assertArrayEquals(new float[]{0.3f, 0.4f},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), SMALL_DELTA);
+
+ assertArrayEquals(new float[]{0.0f, 95},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA);
+ assertArrayEquals(new float[]{0.35f, 0.45f},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA);
+
+ assertArrayEquals(new float[]{0.0f, 100},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), ZERO_DELTA);
+ assertArrayEquals(new float[]{0.4f, 0.5f},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DOZE,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), SMALL_DELTA);
}
@Test
@@ -746,9 +785,15 @@
setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
getValidProxSensor(), /* includeIdleMode= */ false));
- assertNull(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels());
+ assertArrayEquals(new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100),
+ brightnessIntToFloat(150)},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA);
assertArrayEquals(new float[]{0, 110, 500},
- mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+ AUTO_BRIGHTNESS_MODE_DEFAULT,
+ Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA);
assertArrayEquals(new float[]{2, 200, 600},
mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA);
}
@@ -1138,6 +1183,46 @@
+ "</point>\n"
+ "</map>\n"
+ "</luxToBrightnessMapping>\n"
+ + "<luxToBrightnessMapping>\n"
+ + "<setting>dim</setting>\n"
+ + "<map>\n"
+ + "<point>\n"
+ + "<first>0</first>\n"
+ + "<second>0.3</second>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<first>90</first>\n"
+ + "<second>0.4</second>\n"
+ + "</point>\n"
+ + "</map>\n"
+ + "</luxToBrightnessMapping>\n"
+ + "<luxToBrightnessMapping>\n"
+ + "<mode>doze</mode>\n"
+ + "<map>\n"
+ + "<point>\n"
+ + "<first>0</first>\n"
+ + "<second>0.35</second>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<first>95</first>\n"
+ + "<second>0.45</second>\n"
+ + "</point>\n"
+ + "</map>\n"
+ + "</luxToBrightnessMapping>\n"
+ + "<luxToBrightnessMapping>\n"
+ + "<mode>doze</mode>\n"
+ + "<setting>bright</setting>\n"
+ + "<map>\n"
+ + "<point>\n"
+ + "<first>0</first>\n"
+ + "<second>0.4</second>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<first>100</first>\n"
+ + "<second>0.5</second>\n"
+ + "</point>\n"
+ + "</map>\n"
+ + "</luxToBrightnessMapping>\n"
+ "</autoBrightness>\n"
+ getPowerThrottlingConfig()
+ "<highBrightnessMode enabled=\"true\">\n"
@@ -1435,6 +1520,10 @@
when(mResources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels))
.thenReturn(screenBrightnessLevelLux);
+ int[] screenBrightnessLevels = new int[]{50, 100, 150};
+ when(mResources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
+ .thenReturn(screenBrightnessLevels);
// Thresholds
// Config.xml requires the levels arrays to be of length N and the thresholds arrays to be
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 dea838d..fbb14c3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -88,4 +89,12 @@
verify(mDisplayPowerController).setBrightnessFromOffload(brightness);
}
+
+ @Test
+ public void testBlockScreenOn() {
+ Runnable unblocker = () -> {};
+ mSession.blockScreenOn(unblocker);
+
+ verify(mDisplayOffloader).onBlockingScreenOn(eq(unblocker));
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 02bd35a..4cc68cf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -18,6 +18,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import static org.junit.Assert.assertNotNull;
@@ -1568,6 +1570,56 @@
eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
+ @Test
+ public void testSwitchToDozeAutoBrightnessMode() {
+ when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // One triggered by handleBrightnessModeChange, another triggered by requestPowerState
+ verify(mHolder.automaticBrightnessController, times(2))
+ .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+
+ // Back to default mode
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+ }
+
+ @Test
+ public void testDoesNotSwitchFromIdleToDozeAutoBrightnessMode() {
+ when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+ when(mHolder.automaticBrightnessController.isInIdleMode()).thenReturn(true);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.automaticBrightnessController, never())
+ .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+ }
+
+ @Test
+ public void testDoesNotSwitchDozeAutoBrightnessModeIfFeatureFlagOff() {
+ when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(false);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.automaticBrightnessController, never())
+ .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -1853,7 +1905,7 @@
}
@Override
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
return mBrightnessMappingStrategy;
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 64cdac4..943862f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1669,7 +1669,7 @@
}
@Override
- BrightnessMappingStrategy getDefaultModeBrightnessMapper(Resources resources,
+ BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
DisplayDeviceConfig displayDeviceConfig,
DisplayWhiteBalanceController displayWhiteBalanceController) {
return mBrightnessMappingStrategy;
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 49fa254..dafbbb3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
@@ -23,8 +23,10 @@
import org.junit.Test
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
+import java.util.concurrent.Executor
@SmallTest
class DisplayPowerStateTest {
@@ -36,15 +38,21 @@
private val mockBlanker = mock<DisplayBlanker>()
private val mockColorFade = mock<ColorFade>()
+ private val mockExecutor = mock<Executor>()
@Before
fun setUp() {
- displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON)
+ displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON,
+ mockExecutor)
}
@Test
fun `destroys ColorFade on stop`() {
displayPowerState.stop()
+ val runnableCaptor = argumentCaptor<Runnable>()
+
+ verify(mockExecutor).execute(runnableCaptor.capture())
+ runnableCaptor.firstValue.run()
verify(mockColorFade).destroy()
}
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 f36854b..00f9892 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -206,6 +206,9 @@
when(mMockedResources.getIntArray(
com.android.internal.R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
.thenReturn(new int[]{});
+ when(mMockedResources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
+ .thenReturn(new int[]{});
doReturn(true).when(mFlags).isDisplayOffloadEnabled();
initDisplayOffloadSession();
}
@@ -1235,6 +1238,9 @@
@Override
public void stopOffload() {}
+
+ @Override
+ public void onBlockingScreenOn(Runnable unblocker) {}
});
mDisplayOffloadSession = new DisplayOffloadSessionImpl(mDisplayOffloader,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index 8d8274c..87fc7a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -122,6 +122,16 @@
}
@Test
+ public void testRegisterHdrListener_ZeroMinHdrPercent() {
+ IBinder otherBinder = mock(IBinder.class);
+ mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT,
+ /* minimumHdrPercentOfScreen= */ 0, otherBinder);
+
+ verify(mMockHdrInfoListener).unregister(mMockBinder);
+ verify(mMockHdrInfoListener).register(otherBinder);
+ }
+
+ @Test
public void testRegisterNotCalledIfHbmConfigIsMissing() {
IBinder otherBinder = mock(IBinder.class);
mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, -1, otherBinder);
diff --git a/services/tests/media/OWNERS b/services/tests/media/OWNERS
new file mode 100644
index 0000000..160767a6
--- /dev/null
+++ b/services/tests/media/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137631
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/services/tests/media/mediarouterservicetest/Android.bp b/services/tests/media/mediarouterservicetest/Android.bp
new file mode 100644
index 0000000..aed3af6
--- /dev/null
+++ b/services/tests/media/mediarouterservicetest/Android.bp
@@ -0,0 +1,39 @@
+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: "MediaRouterServiceTests",
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "junit",
+ "platform-test-annotations",
+ "services.core",
+ "truth",
+ ],
+
+ platform_apis: true,
+
+ test_suites: [
+ // "device-tests",
+ "general-tests",
+ ],
+
+ certificate: "platform",
+ dxflags: ["--multi-dex"],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/media/mediarouterservicetest/AndroidManifest.xml b/services/tests/media/mediarouterservicetest/AndroidManifest.xml
new file mode 100644
index 0000000..fe65f86
--- /dev/null
+++ b/services/tests/media/mediarouterservicetest/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.media.tests">
+
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+
+ <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.server.media.tests"
+ android:label="Frameworks Services Tests"/>
+</manifest>
diff --git a/services/tests/media/mediarouterservicetest/AndroidTest.xml b/services/tests/media/mediarouterservicetest/AndroidTest.xml
new file mode 100644
index 0000000..b065681
--- /dev/null
+++ b/services/tests/media/mediarouterservicetest/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs MediaRouter Service tests.">
+ <option name="test-tag" value="MediaRouterServiceTests" />
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="MediaRouterServiceTests.apk"/>
+ <option name="install-arg" value="-t" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.InstrumentationTest" >
+ <option name="package" value="com.android.server.media.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
new file mode 100644
index 0000000..8f5d125
--- /dev/null
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -0,0 +1,341 @@
+/*
+ * 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.media;
+
+import static com.android.server.media.AudioRoutingUtils.ATTRIBUTES_MEDIA;
+import static com.android.server.media.AudioRoutingUtils.getMediaAudioProductStrategy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioDevicePort;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.MediaRoute2Info;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class AudioManagerRouteControllerTest {
+
+ private static final String FAKE_ROUTE_NAME = "fake name";
+ private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER =
+ createAudioDeviceInfo(
+ AudioSystem.DEVICE_OUT_SPEAKER, "name_builtin", /* address= */ null);
+ private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET =
+ createAudioDeviceInfo(
+ AudioSystem.DEVICE_OUT_WIRED_HEADSET, "name_wired_hs", /* address= */ null);
+ private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP =
+ createAudioDeviceInfo(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "name_a2dp", /* address= */ "12:34:45");
+
+ private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_BUILTIN_EARPIECE =
+ createAudioDeviceInfo(
+ AudioSystem.DEVICE_OUT_EARPIECE, /* name= */ null, /* address= */ null);
+
+ private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_NO_NAME =
+ createAudioDeviceInfo(
+ AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+ /* name= */ null,
+ /* address= */ null);
+
+ private AudioDeviceInfo mSelectedAudioDeviceInfo;
+ private Set<AudioDeviceInfo> mAvailableAudioDeviceInfos;
+ @Mock private AudioManager mMockAudioManager;
+ @Mock private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+ private AudioManagerRouteController mControllerUnderTest;
+ private AudioDeviceCallback mAudioDeviceCallback;
+ private AudioProductStrategy mMediaAudioProductStrategy;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Resources mockResources = Mockito.mock(Resources.class);
+ when(mockResources.getText(anyInt())).thenReturn(FAKE_ROUTE_NAME);
+ Context realContext = InstrumentationRegistry.getInstrumentation().getContext();
+ Context mockContext = Mockito.mock(Context.class);
+ when(mockContext.getResources()).thenReturn(mockResources);
+ // The bluetooth stack needs the application info, but we cannot use a spy because the
+ // concrete class is package private, so we just return the application info through the
+ // mock.
+ when(mockContext.getApplicationInfo()).thenReturn(realContext.getApplicationInfo());
+
+ // Setup the initial state so that the route controller is created in a sensible state.
+ mSelectedAudioDeviceInfo = FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER;
+ mAvailableAudioDeviceInfos = Set.of(FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER);
+ updateMockAudioManagerState();
+ mMediaAudioProductStrategy = getMediaAudioProductStrategy();
+
+ BluetoothAdapter btAdapter =
+ realContext.getSystemService(BluetoothManager.class).getAdapter();
+ mControllerUnderTest =
+ new AudioManagerRouteController(
+ mockContext,
+ mMockAudioManager,
+ Looper.getMainLooper(),
+ mMediaAudioProductStrategy,
+ btAdapter,
+ mOnDeviceRouteChangedListener);
+ mControllerUnderTest.start(UserHandle.CURRENT_OR_SELF);
+
+ ArgumentCaptor<AudioDeviceCallback> deviceCallbackCaptor =
+ ArgumentCaptor.forClass(AudioDeviceCallback.class);
+ verify(mMockAudioManager)
+ .registerAudioDeviceCallback(deviceCallbackCaptor.capture(), any());
+ mAudioDeviceCallback = deviceCallbackCaptor.getValue();
+
+ // We clear any invocations during setup.
+ clearInvocations(mOnDeviceRouteChangedListener);
+ }
+
+ @Test
+ public void getSelectedRoute_afterDevicesConnect_returnsRightSelectedRoute() {
+ assertThat(mControllerUnderTest.getSelectedRoute().getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP);
+ verify(mOnDeviceRouteChangedListener).onDeviceRouteChanged();
+ assertThat(mControllerUnderTest.getSelectedRoute().getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ null, // Selected device doesn't change.
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+ assertThat(mControllerUnderTest.getSelectedRoute().getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+ }
+
+ @Test
+ public void getSelectedRoute_afterDeviceRemovals_returnsExpectedRoutes() {
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP,
+ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+ verify(mOnDeviceRouteChangedListener).onDeviceRouteChanged();
+
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP);
+ verify(mOnDeviceRouteChangedListener, times(2)).onDeviceRouteChanged();
+ assertThat(mControllerUnderTest.getSelectedRoute().getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+
+ removeAvailableAudioDeviceInfos(
+ /* newSelectedDevice= */ null,
+ /* devicesToRemove...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+ assertThat(mControllerUnderTest.getSelectedRoute().getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+
+ removeAvailableAudioDeviceInfos(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER,
+ /* devicesToRemove...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+ assertThat(mControllerUnderTest.getSelectedRoute().getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ }
+
+ @Test
+ public void onAudioDevicesAdded_clearsAudioRoutingPoliciesCorrectly() {
+ clearInvocations(mMockAudioManager);
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ null, // Selected device doesn't change.
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_BUILTIN_EARPIECE);
+ verifyNoMoreInteractions(mMockAudioManager);
+
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP);
+ verify(mMockAudioManager).removePreferredDeviceForStrategy(mMediaAudioProductStrategy);
+ }
+
+ @Test
+ public void getAvailableDevices_ignoresInvalidMediaOutputs() {
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ null, // Selected device doesn't change.
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_BUILTIN_EARPIECE);
+ verifyNoMoreInteractions(mOnDeviceRouteChangedListener);
+ assertThat(
+ mControllerUnderTest.getAvailableRoutes().stream()
+ .map(MediaRoute2Info::getType)
+ .toList())
+ .containsExactly(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ assertThat(mControllerUnderTest.getSelectedRoute().getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ }
+
+ @Test
+ public void transferTo_setsTheExpectedRoutingPolicy() {
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP,
+ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+ MediaRoute2Info builtInSpeakerRoute =
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ mControllerUnderTest.transferTo(builtInSpeakerRoute.getId());
+ verify(mMockAudioManager)
+ .setPreferredDeviceForStrategy(
+ mMediaAudioProductStrategy,
+ createAudioDeviceAttribute(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER));
+
+ MediaRoute2Info wiredHeadsetRoute =
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET);
+ mControllerUnderTest.transferTo(wiredHeadsetRoute.getId());
+ verify(mMockAudioManager)
+ .setPreferredDeviceForStrategy(
+ mMediaAudioProductStrategy,
+ createAudioDeviceAttribute(AudioDeviceInfo.TYPE_WIRED_HEADSET));
+ }
+
+ @Test
+ public void updateVolume_propagatesCorrectlyToRouteInfo() {
+ when(mMockAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)).thenReturn(2);
+ when(mMockAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)).thenReturn(3);
+ when(mMockAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)).thenReturn(1);
+ when(mMockAudioManager.isVolumeFixed()).thenReturn(false);
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+
+ MediaRoute2Info selectedRoute = mControllerUnderTest.getSelectedRoute();
+ assertThat(selectedRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADSET);
+ assertThat(selectedRoute.getVolume()).isEqualTo(2);
+ assertThat(selectedRoute.getVolumeMax()).isEqualTo(3);
+ assertThat(selectedRoute.getVolumeHandling())
+ .isEqualTo(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE);
+
+ MediaRoute2Info onlyTransferrableRoute =
+ mControllerUnderTest.getAvailableRoutes().stream()
+ .filter(it -> !it.equals(selectedRoute))
+ .findAny()
+ .orElseThrow();
+ assertThat(onlyTransferrableRoute.getType())
+ .isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ assertThat(onlyTransferrableRoute.getVolume()).isEqualTo(0);
+ assertThat(onlyTransferrableRoute.getVolumeMax()).isEqualTo(0);
+ assertThat(onlyTransferrableRoute.getVolume()).isEqualTo(0);
+ assertThat(onlyTransferrableRoute.getVolumeHandling())
+ .isEqualTo(MediaRoute2Info.PLAYBACK_VOLUME_FIXED);
+
+ when(mMockAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)).thenReturn(0);
+ when(mMockAudioManager.isVolumeFixed()).thenReturn(true);
+ mControllerUnderTest.updateVolume(0);
+ MediaRoute2Info newSelectedRoute = mControllerUnderTest.getSelectedRoute();
+ assertThat(newSelectedRoute.getVolume()).isEqualTo(0);
+ assertThat(newSelectedRoute.getVolumeHandling())
+ .isEqualTo(MediaRoute2Info.PLAYBACK_VOLUME_FIXED);
+ }
+
+ @Test
+ public void getAvailableRoutes_whenNoProductNameIsProvided_usesTypeToPopulateName() {
+ assertThat(mControllerUnderTest.getSelectedRoute().getName().toString())
+ .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER.getProductName().toString());
+
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_NO_NAME,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_NO_NAME);
+
+ MediaRoute2Info selectedRoute = mControllerUnderTest.getSelectedRoute();
+ assertThat(selectedRoute.getName().toString()).isEqualTo(FAKE_ROUTE_NAME);
+ }
+
+ // Internal methods.
+
+ @NonNull
+ private MediaRoute2Info getAvailableRouteWithType(int type) {
+ return mControllerUnderTest.getAvailableRoutes().stream()
+ .filter(it -> it.getType() == type)
+ .findFirst()
+ .orElseThrow();
+ }
+
+ private void addAvailableAudioDeviceInfo(
+ @Nullable AudioDeviceInfo newSelectedDevice, AudioDeviceInfo... newAvailableDevices) {
+ Set<AudioDeviceInfo> newAvailableDeviceInfos = new HashSet<>(mAvailableAudioDeviceInfos);
+ newAvailableDeviceInfos.addAll(List.of(newAvailableDevices));
+ mAvailableAudioDeviceInfos = newAvailableDeviceInfos;
+ if (newSelectedDevice != null) {
+ mSelectedAudioDeviceInfo = newSelectedDevice;
+ }
+ updateMockAudioManagerState();
+ mAudioDeviceCallback.onAudioDevicesAdded(newAvailableDevices);
+ }
+
+ private void removeAvailableAudioDeviceInfos(
+ @Nullable AudioDeviceInfo newSelectedDevice, AudioDeviceInfo... devicesToRemove) {
+ Set<AudioDeviceInfo> newAvailableDeviceInfos = new HashSet<>(mAvailableAudioDeviceInfos);
+ List.of(devicesToRemove).forEach(newAvailableDeviceInfos::remove);
+ mAvailableAudioDeviceInfos = newAvailableDeviceInfos;
+ if (newSelectedDevice != null) {
+ mSelectedAudioDeviceInfo = newSelectedDevice;
+ }
+ updateMockAudioManagerState();
+ mAudioDeviceCallback.onAudioDevicesRemoved(devicesToRemove);
+ }
+
+ private void updateMockAudioManagerState() {
+ when(mMockAudioManager.getDevicesForAttributes(ATTRIBUTES_MEDIA))
+ .thenReturn(
+ List.of(createAudioDeviceAttribute(mSelectedAudioDeviceInfo.getType())));
+ when(mMockAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
+ .thenReturn(mAvailableAudioDeviceInfos.toArray(new AudioDeviceInfo[0]));
+ }
+
+ private static AudioDeviceAttributes createAudioDeviceAttribute(int type) {
+ // Address is unused.
+ return new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, type, /* address= */ "");
+ }
+
+ private static AudioDeviceInfo createAudioDeviceInfo(
+ int type, @NonNull String name, @NonNull String address) {
+ return new AudioDeviceInfo(AudioDevicePort.createForTesting(type, name, address));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2f909f8..fcb3caa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -824,24 +824,24 @@
final BroadcastOptions options = BroadcastOptions.makeWithDeferUntilActive(true);
broadcastIntent(intent1, null, true);
- assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER),
+ assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, TEST_USER),
StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
- assertNull(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER));
- assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER));
+ assertNull(mAms.getStickyBroadcastsForTest(TEST_ACTION2, TEST_USER));
+ assertNull(mAms.getStickyBroadcastsForTest(TEST_ACTION3, TEST_USER));
broadcastIntent(intent2, options.toBundle(), true);
- assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER),
+ assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, TEST_USER),
StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
- assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER),
+ assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION2, TEST_USER),
StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN));
- assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER));
+ assertNull(mAms.getStickyBroadcastsForTest(TEST_ACTION3, TEST_USER));
broadcastIntent(intent3, null, true);
- assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER),
+ assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, TEST_USER),
StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
- assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER),
+ assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION2, TEST_USER),
StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN));
- assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER),
+ assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION3, TEST_USER),
StickyBroadcast.create(intent3, false, Process.myUid(), PROCESS_STATE_UNKNOWN));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 115a5b0..e7aaed4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -67,6 +67,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -1878,6 +1879,32 @@
}
@Test
+ public void testReplacePending_withSingletonReceiver() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_PHONE);
+ final ProcessRecord systemApp = makeActiveProcessRecord(PACKAGE_ANDROID, PROCESS_SYSTEM);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
+ .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ final ResolveInfo systemReceiverA = makeManifestReceiver(PACKAGE_ANDROID, PROCESS_SYSTEM,
+ CLASS_BLUE, USER_SYSTEM);
+ final ResolveInfo systemReceiverB = makeManifestReceiver(PACKAGE_ANDROID, PROCESS_SYSTEM,
+ CLASS_BLUE, USER_GUEST);
+
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+ systemReceiverA, systemReceiverB)));
+
+ assertEquals("Unexpected userId for receiverA", USER_SYSTEM,
+ UserHandle.getUserId(systemReceiverA.activityInfo.applicationInfo.uid));
+ assertEquals("Unexpected userId for receiverB", USER_SYSTEM,
+ UserHandle.getUserId(systemReceiverB.activityInfo.applicationInfo.uid));
+
+ waitForIdle();
+
+ verifyScheduleReceiver(times(2), systemApp, airplane);
+ }
+
+ @Test
public void testIdleAndBarrier() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 3b83e3c..fc2e5b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -40,6 +40,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
@@ -109,6 +110,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.Supplier;
@RunWith(AndroidJUnit4.class)
@@ -126,6 +128,8 @@
private MockitoSession mMockingSession;
private String mPackageName;
+ private Map<String, Integer> mPackageCategories;
+ private Map<String, Integer> mPackageUids;
private TestLooper mTestLooper;
@Mock
private PackageManager mMockPackageManager;
@@ -229,34 +233,50 @@
.strictness(Strictness.WARN)
.startMocking();
mMockContext = new MockContext(InstrumentationRegistry.getContext());
+ mPackageCategories = new HashMap<>();
+ mPackageUids = new HashMap<>();
mPackageName = mMockContext.getPackageName();
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_GAME);
+ LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_GAME_DEFAULT_FRAME_RATE);
+ mSetFlagsRule.disableFlags(Flags.FLAG_DISABLE_GAME_MODE_WHEN_APP_TOP);
+ }
+
+ private void mockAppCategory(String packageName, int packageUid,
+ @ApplicationInfo.Category int category)
+ throws Exception {
+ reset(mMockPackageManager);
+ mPackageCategories.put(packageName, category);
+ mPackageUids.put(packageName, packageUid);
+ final List<PackageInfo> packageInfos = new ArrayList<>();
+ for (Map.Entry<String, Integer> entry : mPackageCategories.entrySet()) {
+
+ packageName = entry.getKey();
+ packageUid = mPackageUids.get(packageName);
+ category = entry.getValue();
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ applicationInfo.category = category;
+ when(mMockPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = packageName;
+ pi.applicationInfo = applicationInfo;
+ packageInfos.add(pi);
+
+ when(mMockPackageManager.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(
+ packageUid);
+ when(mMockPackageManager.getPackagesForUid(packageUid)).thenReturn(
+ new String[]{packageName});
+ }
+ when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
+ .thenReturn(packageInfos);
final Resources resources =
InstrumentationRegistry.getInstrumentation().getContext().getResources();
when(mMockPackageManager.getResourcesForApplication(anyString()))
.thenReturn(resources);
- when(mMockPackageManager.getPackageUidAsUser(mPackageName, USER_ID_1)).thenReturn(
- DEFAULT_PACKAGE_UID);
- LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
-
- mSetFlagsRule.enableFlags(Flags.FLAG_GAME_DEFAULT_FRAME_RATE);
- }
-
- private void mockAppCategory(String packageName, @ApplicationInfo.Category int category)
- throws Exception {
- reset(mMockPackageManager);
- final ApplicationInfo gameApplicationInfo = new ApplicationInfo();
- gameApplicationInfo.category = category;
- gameApplicationInfo.packageName = packageName;
- final PackageInfo pi = new PackageInfo();
- pi.packageName = packageName;
- pi.applicationInfo = gameApplicationInfo;
- final List<PackageInfo> packages = new ArrayList<>();
- packages.add(pi);
- when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
- .thenReturn(packages);
- when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
- .thenReturn(gameApplicationInfo);
}
@After
@@ -508,7 +528,7 @@
@Test
public void testGetGameMode_nonGame() throws Exception {
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_AUDIO);
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
mockModifyGameModeGranted();
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
@@ -780,7 +800,7 @@
@Test
public void testDeviceConfig_nonGame() throws Exception {
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_AUDIO);
mockDeviceConfigAll();
mockModifyGameModeGranted();
checkReportedAvailableGameModes(createServiceAndStartUser(USER_ID_1));
@@ -1588,7 +1608,7 @@
@Test
public void testSetGameState_nonGame() throws Exception {
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_AUDIO);
mockDeviceConfigNone();
mockModifyGameModeGranted();
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
@@ -1611,14 +1631,14 @@
gameManagerService.addGameStateListener(mockListener);
verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_AUDIO);
GameState gameState = new GameState(true, GameState.MODE_NONE);
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
mTestLooper.dispatchAll();
verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt());
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_GAME);
gameState = new GameState(true, GameState.MODE_NONE);
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
@@ -1654,7 +1674,7 @@
gameManagerService.addGameStateListener(mockListener);
gameManagerService.removeGameStateListener(mockListener);
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_GAME);
GameState gameState = new GameState(false, GameState.MODE_CONTENT);
gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE));
@@ -1670,13 +1690,17 @@
return output;
}
- private void mockInterventionListForMultipleUsers() {
+ private void mockInterventionListForMultipleUsers() throws Exception {
final String[] packageNames = new String[]{"com.android.app0",
"com.android.app1", "com.android.app2"};
+ int i = 1;
+ for (String p : packageNames) {
+ mockAppCategory(p, DEFAULT_PACKAGE_UID + i++, ApplicationInfo.CATEGORY_GAME);
+ }
final ApplicationInfo[] applicationInfos = new ApplicationInfo[3];
final PackageInfo[] pis = new PackageInfo[3];
- for (int i = 0; i < 3; ++i) {
+ for (i = 0; i < 3; ++i) {
applicationInfos[i] = new ApplicationInfo();
applicationInfos[i].category = ApplicationInfo.CATEGORY_GAME;
applicationInfos[i].packageName = packageNames[i];
@@ -1717,7 +1741,6 @@
new Injector());
startUser(gameManagerService, USER_ID_1);
startUser(gameManagerService, USER_ID_2);
-
gameManagerService.setGameModeConfigOverride("com.android.app0", USER_ID_2,
GameManager.GAME_MODE_PERFORMANCE, "120", "0.6");
gameManagerService.setGameModeConfigOverride("com.android.app2", USER_ID_2,
@@ -1953,7 +1976,7 @@
@Test
public void testUpdateCustomGameModeConfiguration_nonGame() throws Exception {
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_IMAGE);
mockModifyGameModeGranted();
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
gameManagerService.updateCustomGameModeConfiguration(mPackageName,
@@ -2013,13 +2036,14 @@
}
@Test
- public void testWritingSettingFile_onShutdown() throws InterruptedException {
+ public void testWritingSettingFile_onShutdown() throws Exception {
mockModifyGameModeGranted();
mockDeviceConfigAll();
GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onBootCompleted();
startUser(gameManagerService, USER_ID_1);
Thread.sleep(500);
+ mockAppCategory("com.android.app1", DEFAULT_PACKAGE_UID + 1, ApplicationInfo.CATEGORY_GAME);
gameManagerService.setGameModeConfigOverride("com.android.app1", USER_ID_1,
GameManager.GAME_MODE_BATTERY, "60", "0.5");
gameManagerService.setGameMode("com.android.app1", USER_ID_1,
@@ -2259,7 +2283,7 @@
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configString);
mockModifyGameModeGranted();
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+ mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_IMAGE);
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
@@ -2277,9 +2301,7 @@
mockModifyGameModeGranted();
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
String someGamePkg = "some.game";
- mockAppCategory(someGamePkg, ApplicationInfo.CATEGORY_GAME);
- when(mMockPackageManager.getPackageUidAsUser(someGamePkg, USER_ID_1)).thenReturn(
- DEFAULT_PACKAGE_UID + 1);
+ mockAppCategory(someGamePkg, DEFAULT_PACKAGE_UID + 1, ApplicationInfo.CATEGORY_GAME);
gameManagerService.setGameMode(someGamePkg, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
assertEquals(GameManager.GAME_MODE_PERFORMANCE,
gameManagerService.getGameMode(someGamePkg, USER_ID_1));
@@ -2307,24 +2329,11 @@
}
@Test
- public void testGamePowerMode_gamePackage() throws Exception {
- GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
- String[] packages = {mPackageName};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
- gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
- verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
- }
-
- @Test
public void testGamePowerMode_twoGames() throws Exception {
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
- String[] packages1 = {mPackageName};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
String someGamePkg = "some.game";
- String[] packages2 = {someGamePkg};
int somePackageId = DEFAULT_PACKAGE_UID + 1;
- when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+ mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
HashMap<Integer, Boolean> powerState = new HashMap<>();
doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
.when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
@@ -2333,6 +2342,7 @@
assertTrue(powerState.get(Mode.GAME));
gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ assertFalse(powerState.get(Mode.GAME));
gameManagerService.mUidObserver.onUidStateChanged(
somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
assertTrue(powerState.get(Mode.GAME));
@@ -2344,12 +2354,9 @@
@Test
public void testGamePowerMode_twoGamesOverlap() throws Exception {
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
- String[] packages1 = {mPackageName};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages1);
String someGamePkg = "some.game";
- String[] packages2 = {someGamePkg};
int somePackageId = DEFAULT_PACKAGE_UID + 1;
- when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+ mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
@@ -2363,49 +2370,162 @@
}
@Test
- public void testGamePowerMode_released() throws Exception {
- GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
- String[] packages = {mPackageName};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
- gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
- gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
- verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
- }
-
- @Test
public void testGamePowerMode_noPackage() throws Exception {
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
String[] packages = {};
when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
- verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
}
@Test
- public void testGamePowerMode_notAGamePackage() throws Exception {
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+ public void testGamePowerMode_gameAndNotGameApps_flagOn() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DISABLE_GAME_MODE_WHEN_APP_TOP);
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
- String[] packages = {"someapp"};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+
+ String nonGamePkg1 = "not.game1";
+ int nonGameUid1 = DEFAULT_PACKAGE_UID + 1;
+ mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE);
+
+ String nonGamePkg2 = "not.game2";
+ int nonGameUid2 = DEFAULT_PACKAGE_UID + 2;
+ mockAppCategory(nonGamePkg2, nonGameUid2, ApplicationInfo.CATEGORY_IMAGE);
+
+ String gamePkg1 = "game1";
+ int gameUid1 = DEFAULT_PACKAGE_UID + 3;
+ mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME);
+
+ String gamePkg2 = "game2";
+ int gameUid2 = DEFAULT_PACKAGE_UID + 4;
+ mockAppCategory(gamePkg2, gameUid2, ApplicationInfo.CATEGORY_GAME);
+
+ // non-game1 top and background with no-op
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
- verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, true);
+ nonGameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // game1 top to enable game mode
+ gameManagerService.mUidObserver.onUidStateChanged(
+ gameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // non-game1 in foreground to disable game mode
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // non-game2 in foreground with no-op
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid2, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // game 2 in foreground with no-op
+ gameManagerService.mUidObserver.onUidStateChanged(
+ gameUid2, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // non-game 1 in background with no-op
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // non-game2 in background to resume game mode
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid2, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // game 1 in background with no-op
+ gameManagerService.mUidObserver.onUidStateChanged(
+ gameUid1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // game 2 in background to stop game mode
+ gameManagerService.mUidObserver.onUidStateChanged(
+ gameUid2, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
}
@Test
- public void testGamePowerMode_notAGamePackageNotReleased() throws Exception {
- mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_IMAGE);
+ public void testGamePowerMode_gameAndNotGameApps_flagOff() throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DISABLE_GAME_MODE_WHEN_APP_TOP);
GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
- String[] packages = {"someapp"};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
+
+ String nonGamePkg1 = "not.game1";
+ int nonGameUid1 = DEFAULT_PACKAGE_UID + 1;
+ mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE);
+
+ String gamePkg1 = "game1";
+ int gameUid1 = DEFAULT_PACKAGE_UID + 3;
+ mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME);
+
+ // non-game1 top and background with no-op
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ nonGameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
- verify(mMockPowerManager, times(0)).setPowerMode(Mode.GAME, false);
+ nonGameUid1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // game1 top to enable game mode
+ gameManagerService.mUidObserver.onUidStateChanged(
+ gameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // non-game1 in foreground to not interfere
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // non-game 1 in background to not interfere
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // move non-game1 to foreground again
+ gameManagerService.mUidObserver.onUidStateChanged(
+ nonGameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+
+ // with non-game1 on top, game 1 in background to still disable game mode
+ gameManagerService.mUidObserver.onUidStateChanged(
+ gameUid1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
+
+ // with non-game1 on top, game 1 in foreground to still enable game mode
+ gameManagerService.mUidObserver.onUidStateChanged(
+ gameUid1, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
+ verify(mMockPowerManager, never()).setPowerMode(Mode.GAME, false);
+ clearInvocations(mMockPowerManager);
}
@Test
@@ -2432,8 +2552,6 @@
gameManagerService.onBootCompleted();
// Set up a game in the foreground.
- String[] packages = {mPackageName};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
@@ -2448,10 +2566,8 @@
// Adding another game to the foreground.
String anotherGamePkg = "another.game";
- String[] packages2 = {anotherGamePkg};
- mockAppCategory(anotherGamePkg, ApplicationInfo.CATEGORY_GAME);
int somePackageId = DEFAULT_PACKAGE_UID + 1;
- when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+ mockAppCategory(anotherGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
gameManagerService.mUidObserver.onUidStateChanged(
somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
@@ -2484,8 +2600,6 @@
}));
// Set up a game in the foreground.
- String[] packages = {mPackageName};
- when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
@@ -2503,10 +2617,8 @@
// Toggle game default frame rate off.
String anotherGamePkg = "another.game";
- String[] packages2 = {anotherGamePkg};
- mockAppCategory(anotherGamePkg, ApplicationInfo.CATEGORY_GAME);
int somePackageId = DEFAULT_PACKAGE_UID + 1;
- when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
+ mockAppCategory(anotherGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
gameManagerService.mUidObserver.onUidStateChanged(
somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 293003d..32878b3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -67,6 +67,7 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.flags.Flags;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
@@ -78,8 +79,10 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;
@@ -97,6 +100,7 @@
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;
@@ -140,6 +144,9 @@
private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid());
private static final String MISSING_PERMISSION = "missing_permission";
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private Random mRandom;
@Mock
@@ -1347,6 +1354,24 @@
assertThat(mManager.isVisibleToCaller()).isFalse();
}
+ @Test
+ public void testValidateLocation_futureLocation() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LOCATION_VALIDATION);
+ Location location = createLocation(NAME, mRandom);
+ mProvider.setProviderLocation(location);
+
+ assertThat(mPassive.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
+ PERMISSION_FINE)).isEqualTo(location);
+
+ Location futureLocation = createLocation(NAME, mRandom);
+ futureLocation.setElapsedRealtimeNanos(
+ SystemClock.elapsedRealtimeNanos() + TimeUnit.SECONDS.toNanos(2));
+ mProvider.setProviderLocation(futureLocation);
+
+ assertThat(mPassive.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
+ PERMISSION_FINE)).isEqualTo(location);
+ }
+
@MediumTest
@Test
public void testEnableMsl_expectedBehavior() throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index c2b52b4..57326b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -50,9 +50,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Before;
@@ -175,7 +176,7 @@
mPmService.getPlatformPackage(), /* isUpdatedSystemApp */ false);
// isUpdatedSystemApp is ignoreable above, only used for shared library adjustment
return parsedPackage.hideAsFinal();
- } catch (PackageManagerException e) {
+ } catch (PackageParserException e) {
throw new RuntimeException(e);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 7b29e2a..538c0ee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -56,6 +56,7 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder
import com.android.internal.R
+import com.android.internal.pm.parsing.PackageParser2
import com.android.internal.pm.parsing.pkg.PackageImpl
import com.android.internal.pm.parsing.pkg.ParsedPackage
import com.android.internal.pm.pkg.parsing.ParsingPackage
@@ -69,7 +70,6 @@
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.pm.dex.DexManager
import com.android.server.pm.dex.DynamicCodeLogger
-import com.android.server.pm.parsing.PackageParser2
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.resolution.ComponentResolver
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
index da929af..7feafef 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -21,6 +21,7 @@
import android.os.Build
import android.os.Process
import android.util.Log
+import com.android.internal.pm.parsing.PackageParserException
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.testutils.whenever
import java.io.File
@@ -120,7 +121,7 @@
argThat { path: File -> path.path.contains("a.data.package") },
anyInt(),
anyBoolean()))
- .thenThrow(PackageManagerException(
+ .thenThrow(PackageParserException(
PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
val pm = createPackageManagerService()
verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index 6c44fd0..60cedcf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -44,6 +44,7 @@
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
+import java.util.function.BiFunction;
/**
* A unit test for PackageMonitorCallbackHelper implementation.
@@ -78,7 +79,8 @@
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+ null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -91,7 +93,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
- null /* broadcastAllowList */, mHandler);
+ null /* broadcastAllowList */, mHandler, null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
@@ -99,12 +101,41 @@
mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+ null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@Test
+ public void testPackageMonitorCallback_SuspendCallbackCalled() throws Exception {
+ Bundle result = new Bundle();
+ result.putInt(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ result.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[]{FAKE_PACKAGE_NAME});
+ BiFunction<Integer, Bundle, Bundle> filterExtras = (callingUid, intentExtras) -> result;
+
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+ Binder.getCallingUid());
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGES_SUSPENDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
+ null /* broadcastAllowList */, mHandler, filterExtras);
+
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
+ bundleCaptor.capture());
+ Bundle bundle = bundleCaptor.getValue();
+ Intent intent = bundle.getParcelable(
+ PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class);
+ assertThat(intent).isNotNull();
+ assertThat(intent.getAction()).isEqualTo(Intent.ACTION_PACKAGES_SUSPENDED);
+ String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ assertThat(pkgList).isNotNull();
+ assertThat(pkgList.length).isEqualTo(1);
+ assertThat(pkgList[0]).isEqualTo(FAKE_PACKAGE_NAME);
+ }
+
+ @Test
public void testRegisterPackageMonitorCallback_callbackCalled() throws Exception {
IRemoteCallback callback = createMockPackageMonitorCallback();
@@ -112,7 +143,8 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+ null /* filterExtras */);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -136,7 +168,8 @@
// Notify for user 10
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+ null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -239,7 +272,8 @@
mPackageMonitorCallbackHelper.onUserRemoved(10);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler,
+ null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -255,7 +289,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList, mHandler);
+ null /* instantUserIds */, broadcastAllowList, mHandler, null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
}
@@ -271,7 +305,7 @@
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList, mHandler);
+ null /* instantUserIds */, broadcastAllowList, mHandler, null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -287,7 +321,7 @@
Process.SYSTEM_UID);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList, mHandler);
+ null /* instantUserIds */, broadcastAllowList, mHandler, null /* filterExtras */);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
}
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 2d36ff3..d49bc43 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -7,3 +7,4 @@
per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/OWNERS
per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
per-file PinnerServiceTest.java = file:/apct-tests/perftests/OWNERS
+per-file SecurityStateTest.java = file:/SECURITY_STATE_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/audio/FadeConfigurationsTest.java b/services/tests/servicestests/src/com/android/server/audio/FadeConfigurationsTest.java
index 6fca561..69817ad 100644
--- a/services/tests/servicestests/src/com/android/server/audio/FadeConfigurationsTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/FadeConfigurationsTest.java
@@ -16,17 +16,21 @@
package com.android.server.audio;
-import static android.media.AudioAttributes.USAGE_MEDIA;
-import static android.media.AudioAttributes.USAGE_GAME;
-import static android.media.AudioAttributes.USAGE_ASSISTANT;
import static android.media.AudioAttributes.CONTENT_TYPE_SPEECH;
+import static android.media.AudioAttributes.USAGE_ASSISTANT;
+import static android.media.AudioAttributes.USAGE_EMERGENCY;
+import static android.media.AudioAttributes.USAGE_GAME;
+import static android.media.AudioAttributes.USAGE_MEDIA;
import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO;
import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL;
import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK;
import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_UNKNOWN;
+import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
import android.media.AudioAttributes;
+import android.media.FadeManagerConfiguration;
import android.media.VolumeShaper;
+import android.platform.test.flag.junit.SetFlagsRule;
import com.google.common.truth.Expect;
@@ -42,6 +46,7 @@
public final class FadeConfigurationsTest {
private FadeConfigurations mFadeConfigurations;
private static final long DEFAULT_FADE_OUT_DURATION_MS = 2_000;
+ private static final long DEFAULT_FADE_IN_DURATION_MS = 1_000;
private static final long DEFAULT_DELAY_FADE_IN_OFFENDERS_MS = 2000;
private static final long DURATION_FOR_UNFADEABLE_MS = 0;
private static final int TEST_UID_SYSTEM = 1000;
@@ -60,11 +65,19 @@
private static final VolumeShaper.Configuration DEFAULT_FADEOUT_VSHAPE =
new VolumeShaper.Configuration.Builder()
.setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID)
- .setCurve(/* times= */new float[]{0.f, 0.25f, 1.0f} ,
- /* volumes= */new float[]{1.f, 0.65f, 0.0f})
+ .setCurve(/* times= */ new float[]{0.f, 0.25f, 1.0f},
+ /* volumes= */ new float[]{1.f, 0.65f, 0.0f})
.setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
.setDuration(DEFAULT_FADE_OUT_DURATION_MS)
.build();
+ private static final VolumeShaper.Configuration DEFAULT_FADEIN_VSHAPE =
+ new VolumeShaper.Configuration.Builder()
+ .setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID)
+ .setCurve(/* times= */ new float[]{0.f, 0.50f, 1.0f},
+ /* volumes= */ new float[]{0.f, 0.30f, 1.0f})
+ .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
+ .setDuration(DEFAULT_FADE_IN_DURATION_MS)
+ .build();
private static final AudioAttributes TEST_MEDIA_AUDIO_ATTRIBUTE =
new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build();
@@ -72,12 +85,18 @@
new AudioAttributes.Builder().setUsage(USAGE_GAME).build();
private static final AudioAttributes TEST_ASSISTANT_AUDIO_ATTRIBUTE =
new AudioAttributes.Builder().setUsage(USAGE_ASSISTANT).build();
+ private static final AudioAttributes TEST_EMERGENCY_AUDIO_ATTRIBUTE =
+ new AudioAttributes.Builder().setSystemUsage(USAGE_EMERGENCY).build();
+
private static final AudioAttributes TEST_SPEECH_AUDIO_ATTRIBUTE =
new AudioAttributes.Builder().setContentType(CONTENT_TYPE_SPEECH).build();
@Rule
public final Expect expect = Expect.create();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
mFadeConfigurations = new FadeConfigurations();
@@ -156,4 +175,110 @@
.that(mFadeConfigurations.isFadeable(TEST_GAME_AUDIO_ATTRIBUTE, TEST_UID_USER,
PLAYER_TYPE_AAUDIO)).isFalse();
}
+
+ @Test
+ public void testGetFadeableUsages_withFadeManagerConfigurations_equals() {
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION);
+ List<Integer> usageList = List.of(AudioAttributes.USAGE_ALARM,
+ AudioAttributes.USAGE_EMERGENCY);
+ FadeManagerConfiguration fmc = createFadeMgrConfig(/* fadeableUsages= */ usageList,
+ /* unfadeableContentTypes= */ null, /* unfadeableUids= */ null,
+ /* unfadeableAudioAttrs= */ null);
+ FadeConfigurations fadeConfigs = new FadeConfigurations();
+
+ fadeConfigs.setFadeManagerConfiguration(fmc);
+
+ expect.withMessage("Fadeable usages with fade manager configuration")
+ .that(fadeConfigs.getFadeableUsages()).isEqualTo(fmc.getFadeableUsages());
+ }
+
+ @Test
+ public void testGetUnfadeableContentTypes_withFadeManagerConfigurations_equals() {
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION);
+ List<Integer> contentTypesList = List.of(AudioAttributes.CONTENT_TYPE_MUSIC,
+ AudioAttributes.CONTENT_TYPE_MOVIE);
+ FadeManagerConfiguration fmc = createFadeMgrConfig(/* fadeableUsages= */ null,
+ /* unfadeableContentTypes= */ contentTypesList, /* unfadeableUids= */ null,
+ /* unfadeableAudioAttrs= */ null);
+ FadeConfigurations fadeConfigs = new FadeConfigurations();
+
+ fadeConfigs.setFadeManagerConfiguration(fmc);
+
+ expect.withMessage("Unfadeable content types with fade manager configuration")
+ .that(fadeConfigs.getUnfadeableContentTypes())
+ .isEqualTo(fmc.getUnfadeableContentTypes());
+ }
+
+ @Test
+ public void testGetUnfadeableAudioAttributes_withFadeManagerConfigurations_equals() {
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION);
+ List<AudioAttributes> attrsList = List.of(TEST_ASSISTANT_AUDIO_ATTRIBUTE,
+ TEST_EMERGENCY_AUDIO_ATTRIBUTE);
+ FadeManagerConfiguration fmc = createFadeMgrConfig(/* fadeableUsages= */ null,
+ /* unfadeableContentTypes= */ null, /* unfadeableUids= */ null,
+ /* unfadeableAudioAttrs= */ attrsList);
+ FadeConfigurations fadeConfigs = new FadeConfigurations();
+
+ fadeConfigs.setFadeManagerConfiguration(fmc);
+
+ expect.withMessage("Unfadeable audio attributes with fade manager configuration")
+ .that(fadeConfigs.getUnfadeableAudioAttributes())
+ .isEqualTo(fmc.getUnfadeableAudioAttributes());
+ }
+
+ @Test
+ public void testGetUnfadeableUids_withFadeManagerConfigurations_equals() {
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION);
+ List<Integer> uidsList = List.of(TEST_UID_SYSTEM, TEST_UID_USER);
+ FadeManagerConfiguration fmc = createFadeMgrConfig(/* fadeableUsages= */ null,
+ /* unfadeableContentTypes= */ null, /* unfadeableUids= */ uidsList,
+ /* unfadeableAudioAttrs= */ null);
+ FadeConfigurations fadeConfigs = new FadeConfigurations();
+
+ fadeConfigs.setFadeManagerConfiguration(fmc);
+
+ expect.withMessage("Unfadeable uids with fade manager configuration")
+ .that(fadeConfigs.getUnfadeableUids()).isEqualTo(fmc.getUnfadeableUids());
+ }
+
+ private static FadeManagerConfiguration createFadeMgrConfig(List<Integer> fadeableUsages,
+ List<Integer> unfadeableContentTypes, List<Integer> unfadeableUids,
+ List<AudioAttributes> unfadeableAudioAttrs) {
+ FadeManagerConfiguration.Builder builder = new FadeManagerConfiguration.Builder();
+ if (fadeableUsages != null) {
+ builder.setFadeableUsages(fadeableUsages);
+ }
+ if (unfadeableContentTypes != null) {
+ builder.setUnfadeableContentTypes(unfadeableContentTypes);
+ }
+ if (unfadeableUids != null) {
+ builder.setUnfadeableUids(unfadeableUids);
+ }
+ if (unfadeableAudioAttrs != null) {
+ builder.setUnfadeableAudioAttributes(unfadeableAudioAttrs);
+ }
+ if (fadeableUsages != null) {
+ for (int index = 0; index < fadeableUsages.size(); index++) {
+ builder.setFadeOutVolumeShaperConfigForAudioAttributes(
+ createGenericAudioAttributesForUsage(fadeableUsages.get(index)),
+ DEFAULT_FADEOUT_VSHAPE);
+ }
+ }
+ if (fadeableUsages != null) {
+ for (int index = 0; index < fadeableUsages.size(); index++) {
+ builder.setFadeInVolumeShaperConfigForAudioAttributes(
+ createGenericAudioAttributesForUsage(fadeableUsages.get(index)),
+ DEFAULT_FADEIN_VSHAPE);
+ }
+ }
+
+ return builder.build();
+ }
+
+ private static AudioAttributes createGenericAudioAttributesForUsage(int usage) {
+ if (AudioAttributes.isSystemUsage(usage)) {
+ return new AudioAttributes.Builder().setSystemUsage(usage).build();
+ }
+ return new AudioAttributes.Builder().setUsage(usage).build();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
index 65059d5..fa94821 100644
--- a/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/FadeOutManagerTest.java
@@ -23,8 +23,6 @@
import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK;
import static android.media.AudioPlaybackConfiguration.PLAYER_TYPE_UNKNOWN;
-import static org.junit.Assert.assertThrows;
-
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -75,20 +73,11 @@
@Before
public void setUp() {
- mFadeOutManager = new FadeOutManager(new FadeConfigurations());
+ mFadeOutManager = new FadeOutManager();
mContext = ApplicationProvider.getApplicationContext();
}
@Test
- public void constructor_nullFadeConfigurations_fails() {
- Throwable thrown = assertThrows(NullPointerException.class, () -> new FadeOutManager(
- /* FadeConfigurations= */ null));
-
- expect.withMessage("Constructor exception")
- .that(thrown).hasMessageThat().contains("Fade configurations can not be null");
- }
-
- @Test
public void testCanCauseFadeOut_forFaders_returnsTrue() {
FocusRequester winner = createFocusRequester(TEST_MEDIA_AUDIO_ATTRIBUTE, "winning-client",
"unit-test", TEST_UID_USER,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index d2e83e9..9eeb4f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -271,6 +271,7 @@
.thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(null);
mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
index 94cb860..74f8f08 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
@@ -29,7 +29,7 @@
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
@@ -43,10 +43,10 @@
import java.util.ArrayList;
import java.util.List;
-@RequiresFlagsDisabled(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
@Presubmit
@SmallTest
public class SensorOverlaysTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final int SENSOR_ID = 11;
private static final long REQUEST_ID = 8;
@@ -59,6 +59,7 @@
@Before
public void setup() {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
when(mAcquisitionClient.getRequestId()).thenReturn(REQUEST_ID);
when(mAcquisitionClient.hasRequestId()).thenReturn(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
index c9e1c4a..3aaac2e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
@@ -16,21 +16,30 @@
package com.android.server.biometrics.sensors.face;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static android.hardware.face.FaceSensorProperties.TYPE_UNKNOWN;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
+import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -42,6 +51,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.biometrics.Flags;
@@ -66,6 +76,7 @@
private static final int ID_VIRTUAL = 6;
private static final String NAME_DEFAULT = "default";
private static final String NAME_VIRTUAL = "virtual";
+ private static final String OP_PACKAGE_NAME = "FaceServiceTest/SystemUi";
@Rule
public final MockitoRule mMockito = MockitoJUnit.rule();
@@ -78,15 +89,19 @@
@Rule
public final FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule();
@Mock
- FaceProvider mFaceProviderDefault;
+ private FaceProvider mFaceProviderDefault;
@Mock
- FaceProvider mFaceProviderVirtual;
+ private FaceProvider mFaceProviderVirtual;
@Mock
- IFace mDefaultFaceDaemon;
+ private IFace mDefaultFaceDaemon;
@Mock
- IFace mVirtualFaceDaemon;
+ private IFace mVirtualFaceDaemon;
@Mock
- IBiometricService mIBiometricService;
+ private IBiometricService mIBiometricService;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private IFaceServiceReceiver mFaceServiceReceiver;
private final SensorProps mDefaultSensorProps = new SensorProps();
private final SensorProps mVirtualSensorProps = new SensorProps();
@@ -117,7 +132,13 @@
new SensorProps[]{mVirtualSensorProps});
when(mFaceProviderDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault));
when(mFaceProviderVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual));
+ when(mFaceProviderDefault.containsSensor(anyInt()))
+ .thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT));
+ when(mFaceProviderVirtual.containsSensor(anyInt()))
+ .thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL));
+ mContext.getTestablePermissions().setPermission(
+ USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED);
mFaceSensorConfigurations = new FaceSensorConfigurations(false);
mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_DEFAULT, NAME_VIRTUAL},
(name) -> {
@@ -136,7 +157,13 @@
if (NAME_DEFAULT.equals(filteredSensorProps.first)) return mFaceProviderDefault;
if (NAME_VIRTUAL.equals(filteredSensorProps.first)) return mFaceProviderVirtual;
return null;
- }, () -> mIBiometricService);
+ }, () -> mIBiometricService,
+ (name) -> {
+ if (NAME_DEFAULT.equals(name)) return mFaceProviderDefault;
+ if (NAME_VIRTUAL.equals(name)) return mFaceProviderVirtual;
+ return null;
+ },
+ () -> new String[]{NAME_DEFAULT, NAME_VIRTUAL});
}
@Test
@@ -191,6 +218,39 @@
eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any());
}
+ @Test
+ public void testOptionsForAuthentication() throws Exception {
+ FaceAuthenticateOptions faceAuthenticateOptions = new FaceAuthenticateOptions.Builder()
+ .build();
+ initService();
+ mFaceService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
+
+ final long operationId = 5;
+ mFaceService.mServiceWrapper.authenticate(mToken, operationId, mFaceServiceReceiver,
+ faceAuthenticateOptions);
+
+ assertThat(faceAuthenticateOptions.getSensorId()).isEqualTo(ID_DEFAULT);
+ }
+
+ @Test
+ public void testOptionsForDetect() throws Exception {
+ FaceAuthenticateOptions faceAuthenticateOptions = new FaceAuthenticateOptions.Builder()
+ .setOpPackageName(ComponentName.unflattenFromString(OP_PACKAGE_NAME)
+ .getPackageName())
+ .build();
+ mContext.getOrCreateTestableResources().addOverride(
+ R.string.config_keyguardComponent,
+ OP_PACKAGE_NAME);
+ initService();
+ mFaceService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
+ mFaceService.mServiceWrapper.detectFace(mToken, mFaceServiceReceiver,
+ faceAuthenticateOptions);
+
+ assertThat(faceAuthenticateOptions.getSensorId()).isEqualTo(ID_DEFAULT);
+ }
+
private void waitForRegistration() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mFaceService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index f570ba2..88956b6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -40,6 +40,7 @@
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.fingerprint.IFingerprint;
@@ -61,6 +62,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
@@ -92,6 +94,7 @@
private static final String NAME_VIRTUAL = "virtual";
private static final List<FingerprintSensorPropertiesInternal> HIDL_AUTHENTICATORS =
List.of();
+ private static final String OP_PACKAGE_NAME = "FingerprintServiceTest/SystemUi";
@Rule
public final MockitoRule mMockito = MockitoJUnit.rule();
@@ -343,6 +346,24 @@
assertEquals((int) (uidCaptor.getValue()), Binder.getCallingUid());
}
+ @Test
+ public void testOptionsForDetect() throws Exception {
+ FingerprintAuthenticateOptions fingerprintAuthenticateOptions =
+ new FingerprintAuthenticateOptions.Builder()
+ .setOpPackageName(ComponentName.unflattenFromString(
+ OP_PACKAGE_NAME).getPackageName())
+ .build();
+
+ mContext.getOrCreateTestableResources().addOverride(
+ R.string.config_keyguardComponent,
+ OP_PACKAGE_NAME);
+ initServiceWithAndWait(NAME_DEFAULT);
+ mService.mServiceWrapper.detectFingerprint(mToken, mServiceReceiver,
+ fingerprintAuthenticateOptions);
+
+ assertThat(fingerprintAuthenticateOptions.getSensorId()).isEqualTo(ID_DEFAULT);
+ }
+
private FingerprintAuthenticateOptions verifyAuthenticateWithNewRequestId(
FingerprintProvider provider, long operationId) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index c24227f..774ea5b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -161,6 +161,7 @@
@Before
public void setup() {
+ mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
mContext.addMockSystemService(BiometricManager.class, mBiometricManager);
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
when(mBiometricLogger.getAmbientLightProbe(anyBoolean())).thenAnswer(i ->
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 18e6f0a..132b621 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
@@ -164,6 +164,19 @@
}
@Test
+ public void userNotAllowlisted_systemUserCanLaunchBlockedAppStreamingActivity() {
+ GenericWindowPolicyController gwpc = createGwpcWithNoAllowedUsers();
+ gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() {
GenericWindowPolicyController gwpc = createGwpc();
gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index edfe1b4..9b28b81 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -24,10 +24,8 @@
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.companion.virtual.camera.VirtualCameraCallback;
import android.companion.virtual.camera.VirtualCameraConfig;
-import android.companion.virtual.camera.VirtualCameraMetadata;
import android.companion.virtual.camera.VirtualCameraStreamConfig;
import android.companion.virtualcamera.IVirtualCameraService;
import android.companion.virtualcamera.VirtualCameraConfiguration;
@@ -55,11 +53,11 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class VirtualCameraControllerTest {
- private static final int CAMERA_DISPLAY_NAME_RES_ID_1 = 10;
+ private static final String CAMERA_NAME_1 = "Virtual camera 1";
private static final int CAMERA_WIDTH_1 = 100;
private static final int CAMERA_HEIGHT_1 = 200;
- private static final int CAMERA_DISPLAY_NAME_RES_ID_2 = 11;
+ private static final String CAMERA_NAME_2 = "Virtual camera 2";
private static final int CAMERA_WIDTH_2 = 400;
private static final int CAMERA_HEIGHT_2 = 600;
private static final int CAMERA_FORMAT = ImageFormat.YUV_420_888;
@@ -86,7 +84,7 @@
@Test
public void registerCamera_registersCamera() throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1));
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_NAME_1));
ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
@@ -100,7 +98,7 @@
@Test
public void unregisterCamera_unregistersCamera() throws Exception {
VirtualCameraConfig config = createVirtualCameraConfig(
- CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1);
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_NAME_1);
mVirtualCameraController.registerCamera(config);
mVirtualCameraController.unregisterCamera(config);
@@ -111,9 +109,9 @@
@Test
public void close_unregistersAllCameras() throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1));
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_NAME_1));
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_2));
+ CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_NAME_2));
mVirtualCameraController.close();
@@ -131,10 +129,10 @@
}
private VirtualCameraConfig createVirtualCameraConfig(
- int width, int height, int format, int displayNameResId) {
+ int width, int height, int format, String displayName) {
return new VirtualCameraConfig.Builder()
.addStreamConfig(width, height, format)
- .setDisplayNameStringRes(displayNameResId)
+ .setName(displayName)
.setVirtualCameraCallback(mCallbackHandler, createNoOpCallback())
.build();
}
@@ -156,10 +154,6 @@
@NonNull VirtualCameraStreamConfig streamConfig) {}
@Override
- public void onProcessCaptureRequest(
- int streamId, long frameId, @Nullable VirtualCameraMetadata metadata) {}
-
- @Override
public void onStreamClosed(int streamId) {}
};
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraStreamConfigTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraStreamConfigTest.java
new file mode 100644
index 0000000..d9a38eb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraStreamConfigTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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.companion.virtual.camera;
+
+import android.companion.virtual.camera.VirtualCameraStreamConfig;
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class VirtualCameraStreamConfigTest {
+
+ private static final int VGA_WIDTH = 640;
+ private static final int VGA_HEIGHT = 480;
+
+ private static final int QVGA_WIDTH = 320;
+ private static final int QVGA_HEIGHT = 240;
+
+ @Test
+ public void testEquals() {
+ VirtualCameraStreamConfig vgaYuvStreamConfig = new VirtualCameraStreamConfig(VGA_WIDTH,
+ VGA_HEIGHT,
+ ImageFormat.YUV_420_888);
+ VirtualCameraStreamConfig qvgaYuvStreamConfig = new VirtualCameraStreamConfig(QVGA_WIDTH,
+ QVGA_HEIGHT, ImageFormat.YUV_420_888);
+ VirtualCameraStreamConfig vgaRgbaStreamConfig = new VirtualCameraStreamConfig(VGA_WIDTH,
+ VGA_HEIGHT, PixelFormat.RGBA_8888);
+
+ new EqualsTester()
+ .addEqualityGroup(vgaYuvStreamConfig, reparcel(vgaYuvStreamConfig))
+ .addEqualityGroup(qvgaYuvStreamConfig, reparcel(qvgaYuvStreamConfig))
+ .addEqualityGroup(vgaRgbaStreamConfig, reparcel(vgaRgbaStreamConfig))
+ .testEquals();
+ }
+
+ private static VirtualCameraStreamConfig reparcel(VirtualCameraStreamConfig config) {
+ Parcel parcel = Parcel.obtain();
+ try {
+ config.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+ return VirtualCameraStreamConfig.CREATOR.createFromParcel(parcel);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
index fd1abff..d850c73 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
@@ -53,6 +53,12 @@
}
@Test
+ public void getStoredProviders_nullValue_success() {
+ Set<String> providers = CredentialManagerService.getStoredProviders(null, null);
+ assertThat(providers.size()).isEqualTo(0);
+ }
+
+ @Test
public void getStoredProviders_success() {
Set<String> providers =
CredentialManagerService.getStoredProviders(
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 a63d01b..e7da26e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -456,6 +456,14 @@
}
@Test
+ public void avbEnabled_standby_avbDisabled() {
+ enableAbsoluteVolumeBehavior();
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ }
+
+ @Test
public void avbEnabled_cecVolumeDisabled_avbDisabled() {
enableAbsoluteVolumeBehavior();
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index 0d25faf..6dd9171 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -1236,7 +1236,7 @@
// Init InputMethodSettings for the owner user (userId=0), verify calls can get the
// corresponding user's context, contentResolver and the resources configuration.
InputMethodUtils.InputMethodSettings settings = new InputMethodUtils.InputMethodSettings(
- methodMap, 0 /* userId */, true);
+ methodMap, 0 /* userId */);
assertEquals(0, settings.getCurrentUserId());
settings.isShowImeWithHardKeyboardEnabled();
@@ -1247,7 +1247,7 @@
// Calling switchCurrentUser to the secondary user (userId=10), verify calls can get the
// corresponding user's context, contentResolver and the resources configuration.
- settings.switchCurrentUser(10 /* userId */, true);
+ settings.switchCurrentUser(10 /* userId */);
assertEquals(10, settings.getCurrentUserId());
settings.isShowImeWithHardKeyboardEnabled();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index ce15c6d..eb9cce0 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -67,10 +67,10 @@
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
+import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.compat.PlatformCompat;
import com.android.server.integrity.engine.RuleEvaluationEngine;
import com.android.server.integrity.model.IntegrityCheckResult;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.testutils.TestUtils;
diff --git a/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.java
deleted file mode 100644
index 06f117b..0000000
--- a/services/tests/servicestests/src/com/android/server/media/BluetoothRouteControllerTest.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.server.media;
-
-import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
-
-import android.content.Context;
-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 androidx.test.platform.app.InstrumentationRegistry;
-
-import com.google.common.truth.Truth;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class BluetoothRouteControllerTest {
-
- private final BluetoothRouteController.BluetoothRoutesUpdatedListener
- mBluetoothRoutesUpdatedListener =
- () -> {
- // Empty on purpose.
- };
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- private Context mContext;
-
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getInstrumentation().getContext();
- }
-
- @Test
- @RequiresFlagsDisabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
- public void createInstance_audioPoliciesFlagIsDisabled_createsLegacyController() {
- BluetoothRouteController deviceRouteController =
- BluetoothRouteController.createInstance(mContext, mBluetoothRoutesUpdatedListener);
-
- Truth.assertThat(deviceRouteController).isInstanceOf(LegacyBluetoothRouteController.class);
- }
-
- @Test
- @RequiresFlagsEnabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
- public void createInstance_audioPoliciesFlagIsEnabled_createsAudioPoliciesController() {
- BluetoothRouteController deviceRouteController =
- BluetoothRouteController.createInstance(mContext, mBluetoothRoutesUpdatedListener);
-
- Truth.assertThat(deviceRouteController)
- .isInstanceOf(AudioPoliciesBluetoothRouteController.class);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
index 0961b7d..eb78961 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -70,7 +70,6 @@
DeviceRouteController.createInstance(
mContext, Looper.getMainLooper(), mOnDeviceRouteChangedListener);
- Truth.assertThat(deviceRouteController)
- .isInstanceOf(AudioPoliciesDeviceRouteController.class);
+ Truth.assertThat(deviceRouteController).isInstanceOf(AudioManagerRouteController.class);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 1bfd43f..25eedf5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -37,6 +37,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
+import com.android.internal.pm.parsing.PackageParserException;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.TestPackageParser2;
@@ -161,7 +162,8 @@
}
@Test
- public void testParsePackageWithDmFileValid() throws IOException, PackageManagerException {
+ public void testParsePackageWithDmFileValid() throws IOException, PackageManagerException,
+ PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk");
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
@@ -178,7 +180,7 @@
@Test
public void testParsePackageSplitsWithDmFileValid()
- throws IOException, PackageManagerException {
+ throws IOException, PackageManagerException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_base.apk");
@@ -201,7 +203,7 @@
@Test
public void testParsePackageSplitsNoBaseWithDmFileValid()
- throws IOException, PackageManagerException {
+ throws IOException, PackageManagerException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_feature_a.apk");
@@ -219,7 +221,7 @@
}
@Test
- public void testParsePackageWithDmFileInvalid() throws IOException {
+ public void testParsePackageWithDmFileInvalid() throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
Files.createFile(invalidDmFile.toPath());
@@ -242,7 +244,7 @@
@Test
public void testParsePackageSplitsWithDmFileInvalid()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_base.apk");
@@ -268,7 +270,7 @@
@Test
public void testParsePackageWithDmFileInvalidManifest()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*validManifest=*/false);
@@ -283,7 +285,7 @@
@Test
public void testParsePackageWithDmFileEmptyManifest()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*packageName=*/"doesn't matter",
/*versionCode=*/-12345L, /*emptyManifest=*/true, /*validManifest=*/true);
@@ -299,7 +301,7 @@
@Test
public void testParsePackageWithDmFileBadPackageName()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*packageName=*/"bad package name",
DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -315,7 +317,7 @@
@Test
public void testParsePackageWithDmFileBadVersionCode()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
/*versionCode=*/12345L, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -331,7 +333,7 @@
@Test
public void testParsePackageWithDmFileMissingPackageName()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*packageName=*/null,
DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
@@ -347,7 +349,7 @@
@Test
public void testParsePackageWithDmFileMissingVersionCode()
- throws IOException, PackageManagerException {
+ throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
/*versionCode=*/null, /*emptyManifest=*/false, /*validManifest=*/true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 9b4ca2a..6a088d9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -36,7 +36,7 @@
@Postsubmit
class AndroidPackageParsingValidationTest {
companion object {
- private val parser2 = PackageParser2.forParsingFileWithDefaults()
+ private val parser2 = PackageParserUtils.forParsingFileWithDefaults()
private val apks = ((PackageManagerService.SYSTEM_PARTITIONS)
.flatMap {
listOfNotNull(it.privAppFolder, it.appFolder, it.overlayFolder)
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
index c44f583..e420e4b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt
@@ -18,11 +18,12 @@
import android.content.pm.ApplicationInfo
import android.util.ArraySet
+import com.android.internal.pm.parsing.PackageParser2
import java.io.File
class TestPackageParser2(var cacheDir: File? = null) : PackageParser2(
null /* separateProcesses */, null /* displayMetrics */,
- cacheDir /* cacheDir */, object : PackageParser2.Callback() {
+ cacheDir?.let { PackageCacher(cacheDir) }, object : PackageParser2.Callback() {
override fun isChangeEnabled(changeId: Long, appInfo: ApplicationInfo): Boolean {
return true
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 8487903..89056cc 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -64,17 +64,18 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.intThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.Context;
@@ -99,7 +100,6 @@
import android.view.Display;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -110,6 +110,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -125,6 +126,7 @@
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
/**
@@ -175,6 +177,9 @@
private static final int ASSERT_RETRY_ATTEMPTS = 20;
private static final int ASSERT_RETRY_DELAY_MILLISECONDS = 500;
+ @DurationMillisLong
+ private static final long FLUSH_TIMEOUT_MILLISECONDS = 5_000;
+
/** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
private static boolean isPackageInstalled = true;
@@ -563,6 +568,7 @@
mInjector = new MyInjector(myContext, Looper.getMainLooper());
mController = setupController();
setupInitialUsageHistory();
+ flushHandler(mController);
}
@After
@@ -571,12 +577,9 @@
}
@Test
- @FlakyTest(bugId = 185169504)
public void testBoundWidgetPackageExempt() throws Exception {
assumeTrue(mInjector.getContext().getSystemService(AppWidgetManager.class) != null);
- assertEquals(STANDBY_BUCKET_ACTIVE,
- mController.getAppStandbyBucket(PACKAGE_EXEMPTED_1, USER_ID,
- mInjector.mElapsedRealtime, false));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_EXEMPTED_1);
}
@Test
@@ -654,7 +657,6 @@
}
@Test
- @FlakyTest(bugId = 185169504)
public void testIsAppIdle_Charging() throws Exception {
TestParoleListener paroleListener = new TestParoleListener();
mController.addListener(paroleListener);
@@ -662,7 +664,7 @@
setChargingState(mController, false);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
assertFalse(mController.isInParole());
@@ -671,7 +673,7 @@
setChargingState(mController, true);
paroleListener.awaitOnLatch(2000);
assertTrue(paroleListener.getParoleState());
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
assertTrue(mController.isInParole());
@@ -680,14 +682,13 @@
setChargingState(mController, false);
paroleListener.awaitOnLatch(2000);
assertFalse(paroleListener.getParoleState());
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
assertFalse(mController.isInParole());
}
@Test
- @FlakyTest(bugId = 185169504)
public void testIsAppIdle_Enabled() throws Exception {
setChargingState(mController, false);
TestParoleListener paroleListener = new TestParoleListener();
@@ -696,7 +697,7 @@
setAppIdleEnabled(mController, true);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
@@ -711,7 +712,7 @@
setAppIdleEnabled(mController, true);
paroleListener.awaitOnLatch(2000);
assertFalse(paroleListener.getParoleState());
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
}
@@ -723,6 +724,7 @@
private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket,
int userId) {
mInjector.mElapsedRealtime = elapsedTime;
+ flushHandler(controller);
controller.checkIdleStates(userId);
assertEquals(bucket,
controller.getAppStandbyBucket(PACKAGE_1, userId, mInjector.mElapsedRealtime,
@@ -744,50 +746,85 @@
}
private int getStandbyBucket(int userId, AppStandbyController controller, String packageName) {
+ flushHandler(controller);
return controller.getAppStandbyBucket(packageName, userId, mInjector.mElapsedRealtime,
true);
}
+ private List<AppStandbyInfo> getStandbyBuckets(int userId) {
+ flushHandler(mController);
+ return mController.getAppStandbyBuckets(userId);
+ }
+
private int getStandbyBucketReason(String packageName) {
+ flushHandler(mController);
return mController.getAppStandbyBucketReason(packageName, USER_ID,
mInjector.mElapsedRealtime);
}
- private void assertBucket(int bucket) throws InterruptedException {
- assertBucket(bucket, PACKAGE_1);
+ private void waitAndAssertBucket(int bucket, String pkg) {
+ waitAndAssertBucket(mController, bucket, pkg);
}
- private void assertBucket(int bucket, String pkg) throws InterruptedException {
- int retries = 0;
- do {
- if (bucket == getStandbyBucket(mController, pkg)) {
- // Success
- return;
- }
- Thread.sleep(ASSERT_RETRY_DELAY_MILLISECONDS);
- retries++;
- } while(retries < ASSERT_RETRY_ATTEMPTS);
- // try one last time
- assertEquals(bucket, getStandbyBucket(mController, pkg));
+ private void waitAndAssertBucket(AppStandbyController controller, int bucket, String pkg) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(pkg);
+ sb.append(" was not in the ");
+ sb.append(UsageStatsManager.standbyBucketToString(bucket));
+ sb.append(" (");
+ sb.append(bucket);
+ sb.append(") bucket.");
+ waitAndAssertBucket(sb.toString(), controller, bucket, pkg);
}
- private void assertNotBucket(int bucket) throws InterruptedException {
- final String pkg = PACKAGE_1;
+ private void waitAndAssertBucket(String msg, int bucket, String pkg) {
+ waitAndAssertBucket(msg, mController, bucket, pkg);
+ }
+
+ private void waitAndAssertBucket(String msg, AppStandbyController controller, int bucket,
+ String pkg) {
+ waitAndAssertBucket(msg, controller, bucket, USER_ID, pkg);
+ }
+
+ private void waitAndAssertBucket(String msg, AppStandbyController controller, int bucket,
+ int userId,
+ String pkg) {
+ waitUntil(() -> bucket == getStandbyBucket(userId, controller, pkg));
+ assertEquals(msg, bucket, getStandbyBucket(userId, controller, pkg));
+ }
+
+ private void waitAndAssertNotBucket(int bucket, String pkg) {
+ waitAndAssertNotBucket(mController, bucket, pkg);
+ }
+
+ private void waitAndAssertNotBucket(AppStandbyController controller, int bucket, String pkg) {
+ waitUntil(() -> bucket != getStandbyBucket(controller, pkg));
+ assertNotEquals(bucket, getStandbyBucket(controller, pkg));
+ }
+
+ private void waitAndAssertLastNoteEvent(int event) {
+ waitUntil(() -> {
+ flushHandler(mController);
+ return event == mInjector.mLastNoteEvent;
+ });
+ assertEquals(event, mInjector.mLastNoteEvent);
+ }
+
+ // Waits until condition is true or times out.
+ private void waitUntil(BooleanSupplier resultSupplier) {
int retries = 0;
do {
- if (bucket != getStandbyBucket(mController, pkg)) {
- // Success
- return;
+ if (resultSupplier.getAsBoolean()) return;
+ try {
+ Thread.sleep(ASSERT_RETRY_DELAY_MILLISECONDS);
+ } catch (InterruptedException ie) {
+ // Do nothing
}
- Thread.sleep(ASSERT_RETRY_DELAY_MILLISECONDS);
retries++;
- } while(retries < ASSERT_RETRY_ATTEMPTS);
- // try one last time
- assertNotEquals(bucket, getStandbyBucket(mController, pkg));
+ } while (retries < ASSERT_RETRY_ATTEMPTS);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testBuckets() throws Exception {
assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
@@ -820,14 +857,13 @@
}
@Test
- @FlakyTest(bugId = 185169504)
public void testSetAppStandbyBucket() throws Exception {
// For a known package, standby bucket should be set properly
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_TIMEOUT);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// For an unknown package, standby bucket should not be set, hence NEVER is returned
// Ensure the unknown package is not already in history by removing it
@@ -836,21 +872,20 @@
mController.setAppStandbyBucket(PACKAGE_UNKNOWN, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_TIMEOUT);
isPackageInstalled = true; // Reset mocked variable for other tests
- assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+ waitAndAssertBucket(STANDBY_BUCKET_NEVER, PACKAGE_UNKNOWN);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testAppStandbyBucketOnInstallAndUninstall() throws Exception {
// On package install, standby bucket should be ACTIVE
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_UNKNOWN);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_UNKNOWN);
// On uninstall, package should not exist in history and should return a NEVER bucket
mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
- assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+ waitAndAssertBucket(STANDBY_BUCKET_NEVER, PACKAGE_UNKNOWN);
// Ensure uninstalled app is not in history
- List<AppStandbyInfo> buckets = mController.getAppStandbyBuckets(USER_ID);
+ List<AppStandbyInfo> buckets = getStandbyBuckets(USER_ID);
for(AppStandbyInfo bucket : buckets) {
if (bucket.mPackageName.equals(PACKAGE_UNKNOWN)) {
fail("packageName found in app idle history after uninstall.");
@@ -859,7 +894,6 @@
}
@Test
- @FlakyTest(bugId = 185169504)
public void testScreenTimeAndBuckets() throws Exception {
mInjector.setDisplayOn(false);
@@ -876,22 +910,21 @@
// RARE bucket, should fail because the screen wasn't ON.
mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
mController.checkIdleStates(USER_ID);
- assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertNotBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
mInjector.setDisplayOn(true);
assertTimeout(mController, RARE_THRESHOLD + 2 * HOUR_MS + 1, STANDBY_BUCKET_RARE);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testForcedIdle() throws Exception {
mController.forceIdleState(PACKAGE_1, USER_ID, true);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
mController.forceIdleState(PACKAGE_1, USER_ID, false);
- assertEquals(STANDBY_BUCKET_ACTIVE, mController.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
- true));
+
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
}
@@ -901,15 +934,15 @@
.onPropertiesChanged(mInjector.getDeviceConfigProperties());
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime = 1;
rearmQuotaBumpLatch(PACKAGE_1, USER_ID);
reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mController.forceIdleState(PACKAGE_1, USER_ID, true);
reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
assertFalse(mQuotaBumpLatch.await(1, TimeUnit.SECONDS));
}
@@ -917,9 +950,10 @@
public void testNotificationEvent_bucketPromotion_changePromotedBucket() throws Exception {
mInjector.mPropertiesChangedListener
.onPropertiesChanged(mInjector.getDeviceConfigProperties());
+ mInjector.mElapsedRealtime += RARE_THRESHOLD + 1;
mController.forceIdleState(PACKAGE_1, USER_ID, true);
reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
// TODO: Avoid hardcoding these string constants.
mInjector.mSettingsBuilder.setInt("notification_seen_promoted_bucket",
@@ -928,11 +962,10 @@
mInjector.getDeviceConfigProperties());
mController.forceIdleState(PACKAGE_1, USER_ID, true);
reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testNotificationEvent_quotaBump() throws Exception {
mInjector.mSettingsBuilder
.setBoolean("trigger_quota_bump_on_notification_seen", true);
@@ -942,7 +975,7 @@
.onPropertiesChanged(mInjector.getDeviceConfigProperties());
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime = RARE_THRESHOLD * 2;
setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
@@ -951,83 +984,80 @@
reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
assertTrue(mQuotaBumpLatch.await(1, TimeUnit.SECONDS));
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testSlicePinnedEvent() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime = 1;
reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mController.forceIdleState(PACKAGE_1, USER_ID, true);
reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testSlicePinnedPrivEvent() throws Exception {
mController.forceIdleState(PACKAGE_1, USER_ID, true);
reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime, PACKAGE_1);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testPredictionTimedOut() throws Exception {
// Set it to timeout or usage, so that prediction can override it
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Fast forward 12 hours
mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
mController.checkIdleStates(USER_ID);
// Should still be in predicted bucket, since prediction timeout is 1 day since prediction
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Fast forward two more hours
mInjector.mElapsedRealtime += 2 * HOUR_MS;
mController.checkIdleStates(USER_ID);
// Should have now applied prediction timeout
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
// Fast forward RARE bucket
mInjector.mElapsedRealtime += RARE_THRESHOLD;
mController.checkIdleStates(USER_ID);
// Should continue to apply prediction timeout
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
+ @Ignore("b/317086276")
public void testOverrides() throws Exception {
// Can force to NEVER
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_NEVER, PACKAGE_1);
// Prediction can't override FORCED reasons
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_FORCED_BY_USER);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED);
- assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
// Prediction can't override NEVER
mInjector.mElapsedRealtime = 2 * HOUR_MS;
@@ -1035,115 +1065,114 @@
REASON_MAIN_DEFAULT);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
- assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_NEVER, PACKAGE_1);
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_USAGE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_PREDICTED);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Prediction can't remove from RESTRICTED
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Force from user can remove from RESTRICTED
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
// Force from system can remove from RESTRICTED if it was put it in due to system
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_FORCED_BY_SYSTEM);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_FORCED_BY_SYSTEM);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_PREDICTED);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_FORCED_BY_SYSTEM);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Non-user usage can't remove from RESTRICTED
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_USAGE);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_USAGE | REASON_SUB_USAGE_SYSTEM_INTERACTION);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_USAGE | REASON_SUB_USAGE_SYNC_ADAPTER);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_USAGE | REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Explicit user usage can remove from RESTRICTED
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testTimeout() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime = 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// bucketing works after timeout
mInjector.mElapsedRealtime = mController.mPredictionTimeoutMillis - 100;
mController.checkIdleStates(USER_ID);
// Use recent prediction
- assertBucket(STANDBY_BUCKET_FREQUENT);
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
// Way past prediction timeout, use system thresholds
mInjector.mElapsedRealtime = RARE_THRESHOLD;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
/** Test that timeouts still work properly even if invalid configuration values are set. */
@Test
- @FlakyTest(bugId = 185169504)
public void testTimeout_InvalidThresholds() throws Exception {
mInjector.mSettingsBuilder
.setLong("screen_threshold_active", -1)
@@ -1161,19 +1190,19 @@
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime = HOUR_MS;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_FREQUENT);
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
mInjector.mElapsedRealtime = 2 * HOUR_MS;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
mInjector.mElapsedRealtime = 4 * HOUR_MS;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
/**
@@ -1181,74 +1210,72 @@
* timeout has passed.
*/
@Test
- @FlakyTest(bugId = 185169504)
+ @Ignore("b/317086276")
public void testTimeoutBeforeRestricted() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
// Bucket shouldn't change
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// bucketing works after timeout
mInjector.mElapsedRealtime += DAY_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Way past all timeouts. Make sure timeout processing doesn't raise bucket.
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
/**
* Test that an app is put into the RESTRICTED bucket after enough time has passed.
*/
@Test
- @FlakyTest(bugId = 185169504)
public void testRestrictedDelay() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime += mInjector.getAutoRestrictedBucketDelayMs() - 5000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
// Bucket shouldn't change
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// bucketing works after timeout
mInjector.mElapsedRealtime += 6000;
Thread.sleep(6000);
// Enough time has passed. The app should automatically be put into the RESTRICTED bucket.
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
/**
* Test that an app is put into the RESTRICTED bucket after enough time has passed.
*/
@Test
- @FlakyTest(bugId = 185169504)
public void testRestrictedDelay_DelayChange() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mAutoRestrictedBucketDelayMs = 2 * HOUR_MS;
mInjector.mElapsedRealtime += 2 * HOUR_MS - 5000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM);
// Bucket shouldn't change
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// bucketing works after timeout
mInjector.mElapsedRealtime += 6000;
Thread.sleep(6000);
// Enough time has passed. The app should automatically be put into the RESTRICTED bucket.
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
/**
@@ -1256,36 +1283,35 @@
* a low bucket after the RESTRICTED timeout.
*/
@Test
- @FlakyTest(bugId = 185169504)
public void testRestrictedTimeoutOverridesRestoredLowBucketPrediction() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Predict to RARE Not long enough to time out into RESTRICTED.
mInjector.mElapsedRealtime += RARE_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
// Add a short timeout event
mInjector.mElapsedRealtime += 1000;
reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime += 1000;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Long enough that it could have timed out into RESTRICTED. Instead of reverting to
// predicted RARE, should go into RESTRICTED
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Ensure that prediction can still raise it out despite this override.
mInjector.mElapsedRealtime += 1;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
}
/**
@@ -1293,7 +1319,6 @@
* a low bucket after the RESTRICTED timeout.
*/
@Test
- @FlakyTest(bugId = 185169504)
public void testRestrictedTimeoutOverridesPredictionLowBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
@@ -1301,7 +1326,7 @@
mInjector.mElapsedRealtime += RARE_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
mInjector.mElapsedRealtime += 1;
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
@@ -1310,10 +1335,10 @@
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
/**
@@ -1321,261 +1346,250 @@
* interaction.
*/
@Test
- @FlakyTest(bugId = 185169504)
public void testSystemInteractionOverridesRestrictedTimeout() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Long enough that it could have timed out into RESTRICTED.
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Report system interaction.
mInjector.mElapsedRealtime += 1000;
reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Ensure that it's raised out of RESTRICTED for the system interaction elevation duration.
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime += 1000;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Elevation duration over. Should fall back down.
mInjector.mElapsedRealtime += 10 * MINUTE_MS;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testPredictionRaiseFromRestrictedTimeout_highBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Way past all timeouts. App times out into RESTRICTED bucket.
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Since the app timed out into RESTRICTED, prediction should be able to remove from the
// bucket.
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testPredictionRaiseFromRestrictedTimeout_lowBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Way past all timeouts. App times out into RESTRICTED bucket.
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Prediction into a low bucket means no expectation of the app being used, so we shouldn't
// elevate the app from RESTRICTED.
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testCascadingTimeouts() throws Exception {
mInjector.mPropertiesChangedListener
.onPropertiesChanged(mInjector.getDeviceConfigProperties());
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime = 2000 + mController.mStrongUsageTimeoutMillis;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_WORKING_SET);
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
mInjector.mElapsedRealtime = 2000 + mController.mNotificationSeenTimeoutMillis;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_FREQUENT);
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testOverlappingTimeouts() throws Exception {
mInjector.mPropertiesChangedListener
.onPropertiesChanged(mInjector.getDeviceConfigProperties());
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Overlapping USER_INTERACTION before previous one times out
reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000,
PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Still in ACTIVE after first USER_INTERACTION times out
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Both timed out, so NOTIFICATION_SEEN timeout should be effective
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_WORKING_SET);
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testSystemInteractionTimeout() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
// Fast forward to RARE
mInjector.mElapsedRealtime = RARE_THRESHOLD + 100;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
// Trigger a SYSTEM_INTERACTION and verify bucket
reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Verify it's still in ACTIVE close to end of timeout
mInjector.mElapsedRealtime += mController.mSystemInteractionTimeoutMillis - 100;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Verify bucket moves to RARE after timeout
mInjector.mElapsedRealtime += 200;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testInitialForegroundServiceTimeout() throws Exception {
mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
// Make sure app is in NEVER bucket
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_FORCED_BY_USER);
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_NEVER);
+ waitAndAssertBucket(STANDBY_BUCKET_NEVER, PACKAGE_1);
mInjector.mElapsedRealtime += 100;
// Trigger a FOREGROUND_SERVICE_START and verify bucket
reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Verify it's still in ACTIVE close to end of timeout
mInjector.mElapsedRealtime += mController.mInitialForegroundServiceStartTimeoutMillis - 100;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Verify bucket moves to RARE after timeout
mInjector.mElapsedRealtime += 200;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
// Trigger a FOREGROUND_SERVICE_START again
reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
mController.checkIdleStates(USER_ID);
// Bucket should not be immediately elevated on subsequent service starts
- assertBucket(STANDBY_BUCKET_RARE);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testPredictionNotOverridden() throws Exception {
mInjector.mPropertiesChangedListener
.onPropertiesChanged(mInjector.getDeviceConfigProperties());
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime = WORKING_SET_THRESHOLD - 1000;
reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Falls back to WORKING_SET
mInjector.mElapsedRealtime += 5000;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_WORKING_SET);
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
// Predict to ACTIVE
mInjector.mElapsedRealtime += 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// CheckIdleStates should not change the prediction
mInjector.mElapsedRealtime += 1000;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testPredictionStrikesBack() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Predict to FREQUENT
mInjector.mElapsedRealtime = RARE_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED);
- assertBucket(STANDBY_BUCKET_FREQUENT);
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
// Add a short timeout event
mInjector.mElapsedRealtime += 1000;
reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime += 1000;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_ACTIVE);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
// Verify it reverted to predicted
mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD / 2;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_FREQUENT);
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testSystemForcedFlags_NotAddedForUserForce() throws Exception {
final int expectedReason = REASON_MAIN_FORCED_BY_USER;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
}
@Test
- @FlakyTest(bugId = 185169504)
public void testSystemForcedFlags_AddedForSystemForce() throws Exception {
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_DEFAULT);
@@ -1584,13 +1598,13 @@
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_SYSTEM
| REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
| REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
getStandbyBucketReason(PACKAGE_1));
mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Flags should be combined
assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
| REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
@@ -1598,7 +1612,6 @@
}
@Test
- @FlakyTest(bugId = 185169504)
public void testSystemForcedFlags_SystemForceChangesBuckets() throws Exception {
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_DEFAULT);
@@ -1607,14 +1620,14 @@
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_FORCED_BY_SYSTEM
| REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
- assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
| REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
getStandbyBucketReason(PACKAGE_1));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
- assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_1);
// Flags should be combined
assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
| REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
@@ -1623,20 +1636,19 @@
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
// Flags should be combined
assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
getStandbyBucketReason(PACKAGE_1));
mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED);
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Flags should not be combined since the bucket changed.
assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED,
getStandbyBucketReason(PACKAGE_1));
}
@Test
- @FlakyTest(bugId = 185169504)
public void testRestrictApp_MainReason() throws Exception {
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_DEFAULT);
@@ -1644,11 +1656,11 @@
mController.restrictApp(PACKAGE_1, USER_ID, REASON_MAIN_PREDICTED, 0);
// Call should be ignored.
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mController.restrictApp(PACKAGE_1, USER_ID, REASON_MAIN_FORCED_BY_USER, 0);
// Call should go through
- assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
@Test
@@ -1724,15 +1736,15 @@
}
@Test
- @FlakyTest(bugId = 185169504)
public void testUserInteraction_CrossProfile() throws Exception {
mInjector.mRunningUsers = new int[] {USER_ID, USER_ID2, USER_ID3};
mInjector.mCrossProfileTargets = Arrays.asList(USER_HANDLE_USER2);
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertEquals("Cross profile connected package bucket should be elevated on usage",
- STANDBY_BUCKET_ACTIVE, getStandbyBucket(USER_ID2, mController, PACKAGE_1));
- assertEquals("Not Cross profile connected package bucket should not be elevated on usage",
- STANDBY_BUCKET_NEVER, getStandbyBucket(USER_ID3, mController, PACKAGE_1));
+ waitAndAssertBucket("Cross profile connected package bucket should be elevated on usage",
+ mController, STANDBY_BUCKET_ACTIVE, USER_ID2, PACKAGE_1);
+ waitAndAssertBucket(
+ "Not Cross profile connected package bucket should not be elevated on usage",
+ mController, STANDBY_BUCKET_NEVER, USER_ID3, PACKAGE_1);
assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE, USER_ID);
assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE, USER_ID2);
@@ -1742,51 +1754,50 @@
mInjector.mCrossProfileTargets = Collections.emptyList();
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
- assertEquals("No longer cross profile connected package bucket should not be "
- + "elevated on usage",
- STANDBY_BUCKET_WORKING_SET, getStandbyBucket(USER_ID2, mController, PACKAGE_1));
+ waitAndAssertBucket("No longer cross profile connected package bucket should not be "
+ + "elevated on usage", mController, STANDBY_BUCKET_WORKING_SET, USER_ID2,
+ PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testUnexemptedSyncScheduled() throws Exception {
rearmLatch(PACKAGE_1);
mController.addListener(mListener);
- assertEquals("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER,
- getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER,
+ PACKAGE_1);
mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false);
mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS);
- assertEquals("Unexempted sync scheduled should bring the package out of the Never bucket",
- STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket(
+ "Unexempted sync scheduled should bring the package out of the Never bucket",
+ STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
rearmLatch(PACKAGE_1);
mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false);
mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS);
- assertEquals("Unexempted sync scheduled should not elevate a non Never bucket",
- STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket("Unexempted sync scheduled should not elevate a non Never bucket",
+ STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
- @FlakyTest(bugId = 185169504)
public void testExemptedSyncScheduled() throws Exception {
setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
mInjector.mDeviceIdleMode = true;
rearmLatch(PACKAGE_1);
mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS);
- assertEquals("Exempted sync scheduled in doze should set bucket to working set",
- STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket("Exempted sync scheduled in doze should set bucket to working set",
+ STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
mInjector.mDeviceIdleMode = false;
rearmLatch(PACKAGE_1);
mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS);
- assertEquals("Exempted sync scheduled while not in doze should set bucket to active",
- STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket("Exempted sync scheduled while not in doze should set bucket to active",
+ STANDBY_BUCKET_ACTIVE, PACKAGE_1);
}
@Test
@@ -1796,14 +1807,14 @@
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Updates shouldn't change bucket if the app was forced by the system for a non-buggy
// reason.
@@ -1814,11 +1825,11 @@
| REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Updates should change bucket if the app was forced by the system for a buggy reason.
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
@@ -1827,11 +1838,11 @@
REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertNotEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertNotBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Updates shouldn't change bucket if the app was forced by the system for more than just
// a buggy reason.
@@ -1842,13 +1853,13 @@
| REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
getStandbyBucketReason(PACKAGE_1));
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
// Updates shouldn't change bucket if the app was forced by the user.
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
@@ -1857,11 +1868,11 @@
REASON_MAIN_FORCED_BY_USER);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
+ waitAndAssertBucket(STANDBY_BUCKET_RESTRICTED, PACKAGE_1);
}
@Test
@@ -1876,37 +1887,37 @@
mController.setAppStandbyBucket(PACKAGE_SYSTEM_HEADFULL, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_RARE, PACKAGE_SYSTEM_HEADFULL);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_SYSTEM_HEADFULL);
// Make sure headless system apps don't get lowered.
mController.setAppStandbyBucket(PACKAGE_SYSTEM_HEADLESS, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_SYSTEM_HEADLESS);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_SYSTEM_HEADLESS);
// Package 1 doesn't have activities and is headless, but is not a system app, so it can
// be lowered.
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
public void testWellbeingAppElevated() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_WELLBEING);
- assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING);
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
// Make sure the default wellbeing app does not get lowered below WORKING_SET.
mController.setAppStandbyBucket(PACKAGE_WELLBEING, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_WELLBEING);
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_WELLBEING);
// A non default wellbeing app should be able to fall lower than WORKING_SET.
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
@Test
@@ -1914,22 +1925,22 @@
mInjector.mClockApps.add(Pair.create(PACKAGE_1, UID_1));
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_2);
- assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_2);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_2);
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
// Make sure a clock app does not get lowered below WORKING_SET.
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
+ waitAndAssertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_1);
// A non clock app should be able to fall lower than WORKING_SET.
mController.setAppStandbyBucket(PACKAGE_2, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_RARE, PACKAGE_2);
+ waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_2);
}
@Test
@@ -2067,13 +2078,13 @@
public void testBackgroundLocationBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime,
PACKAGE_BACKGROUND_LOCATION);
- assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_BACKGROUND_LOCATION);
+ waitAndAssertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_BACKGROUND_LOCATION);
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
// Make sure PACKAGE_BACKGROUND_LOCATION does not get lowered than STANDBY_BUCKET_FREQUENT.
mController.setAppStandbyBucket(PACKAGE_BACKGROUND_LOCATION, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
- assertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_BACKGROUND_LOCATION);
+ waitAndAssertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_BACKGROUND_LOCATION);
}
@Test
@@ -2083,41 +2094,41 @@
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE);
// Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
// Reset the last event to confirm the method isn't called.
mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_NONE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE);
// Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called.
// Reset the last event to confirm the method isn't called.
mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_NONE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_EXEMPTED,
REASON_MAIN_FORCED_BY_USER);
- assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent);
+ waitAndAssertLastNoteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE);
}
private String getAdminAppsStr(int userId) {
@@ -2187,8 +2198,7 @@
rearmLatch(pkg);
mController.setAppStandbyBucket(pkg, user, bucket, reason);
mStateChangedLatch.await(1, TimeUnit.SECONDS);
- assertEquals("Failed to set package bucket", bucket,
- getStandbyBucket(mController, PACKAGE_1));
+ waitAndAssertBucket("Failed to set package bucket", bucket, PACKAGE_1);
}
private void rearmLatch(String pkgName) {
@@ -2205,4 +2215,12 @@
mLatchUserId = userId;
mQuotaBumpLatch = new CountDownLatch(1);
}
+
+ private void flushHandler(AppStandbyController controller) {
+ assertTrue("Failed to flush handler!", controller.flushHandler(FLUSH_TIMEOUT_MILLISECONDS));
+ // Some AppStandbyController handler messages queue another handler message. Flush again
+ // to catch those as well.
+ assertTrue("Failed to flush handler (the second time)!",
+ controller.flushHandler(FLUSH_TIMEOUT_MILLISECONDS));
+ }
}
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 260ee396..30843d2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -245,8 +245,8 @@
}
@Test
- @TestParameters({"{origin: ORIGIN_USER}", "{origin: ORIGIN_INIT}", "{origin: ORIGIN_INIT_USER}",
- "{origin: ORIGIN_SYSTEM_OR_SYSTEMUI}"})
+ @TestParameters({"{origin: ORIGIN_USER}", "{origin: ORIGIN_INIT}",
+ "{origin: ORIGIN_INIT_USER}"})
public void apply_nightModeWithScreenOn_appliedImmediatelyBasedOnOrigin(ChangeOrigin origin) {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
@@ -263,7 +263,7 @@
@Test
@TestParameters({"{origin: ORIGIN_APP}", "{origin: ORIGIN_RESTORE_BACKUP}",
- "{origin: ORIGIN_UNKNOWN}"})
+ "{origin: ORIGIN_SYSTEM_OR_SYSTEMUI}", "{origin: ORIGIN_UNKNOWN}"})
public void apply_nightModeWithScreenOn_willBeAppliedLaterBasedOnOrigin(ChangeOrigin origin) {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 2868b7e..ae36839 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -68,10 +68,7 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserHandle;
-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.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.INotificationListener;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
@@ -111,7 +108,7 @@
public class NotificationListenersTest extends UiServiceTestCase {
@Rule
- public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
private PackageManager mPm;
@@ -696,8 +693,8 @@
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testListenerTrusted_withPermission() throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
when(mNm.mPackageManager.checkUidPermission(RECEIVE_SENSITIVE_NOTIFICATIONS, mUid1))
.thenReturn(PERMISSION_GRANTED);
ManagedServices.ManagedServiceInfo info = getMockServiceInfo();
@@ -706,8 +703,8 @@
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testListenerTrusted_withSystemSignature() {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
when(mNm.mPackageManagerInternal.isPlatformSigned(mCn1.getPackageName())).thenReturn(true);
ManagedServices.ManagedServiceInfo info = getMockServiceInfo();
mListeners.onServiceAdded(info);
@@ -715,8 +712,8 @@
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testListenerTrusted_withCdmAssociation() throws Exception {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
mNm.mCompanionManager = mock(ICompanionDeviceManager.class);
AssociationInfo assocInfo = mock(AssociationInfo.class);
when(assocInfo.isRevoked()).thenReturn(false);
@@ -731,16 +728,16 @@
}
@Test
- @RequiresFlagsDisabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testListenerTrusted_ifFlagDisabled() {
+ mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
ManagedServices.ManagedServiceInfo info = getMockServiceInfo();
mListeners.onServiceAdded(info);
assertTrue(mListeners.isUidTrusted(mUid1));
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testRedaction_whenPosted() {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
infos.add(getMockServiceInfo());
doReturn(infos).when(mListeners).getServices();
@@ -762,13 +759,11 @@
mListeners.notifyPostedLocked(r, old);
verify(mListeners, atLeast(1)).redactStatusBarNotification(eq(sbn));
verify(mListeners, never()).redactStatusBarNotification(eq(oldSbn));
-
-
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testRedaction_whenPosted_oldRemoved() {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
infos.add(getMockServiceInfo());
doReturn(infos).when(mListeners).getServices();
@@ -795,8 +790,8 @@
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testRedaction_whenRemoved() {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
doReturn(mock(StatusBarNotification.class))
.when(mListeners).redactStatusBarNotification(any());
ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
@@ -816,8 +811,8 @@
}
@Test
- @RequiresFlagsDisabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testRedaction_noneIfFlagDisabled() {
+ mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>();
infos.add(getMockServiceInfo());
doReturn(infos).when(mListeners).getServices();
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 56c75b5..39779b0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,11 +16,14 @@
package com.android.server.notification;
+import static android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
import static android.app.Notification.EXTRA_PICTURE;
import static android.app.Notification.EXTRA_PICTURE_ICON;
@@ -60,6 +63,8 @@
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.PackageManager.FEATURE_TELECOM;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -77,9 +82,14 @@
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.service.notification.Adjustment.KEY_CONTEXTUAL_ACTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
+import static android.service.notification.Condition.SOURCE_CONTEXT;
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
+import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
@@ -101,6 +111,7 @@
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -113,6 +124,7 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.anyBoolean;
@@ -213,9 +225,6 @@
import android.os.WorkSource;
import android.permission.PermissionManager;
import android.platform.test.annotations.EnableFlags;
-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 android.platform.test.rule.DeniedDevices;
import android.platform.test.rule.DeviceProduct;
@@ -224,6 +233,7 @@
import android.provider.MediaStore;
import android.provider.Settings;
import android.service.notification.Adjustment;
+import android.service.notification.Condition;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.DeviceEffectsApplier;
import android.service.notification.INotificationListener;
@@ -293,6 +303,7 @@
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -342,6 +353,11 @@
private static final String SCHEME_TIMEOUT = "timeout";
private static final String REDACTED_TEXT = "redacted text";
+ private static final AutomaticZenRule SOME_ZEN_RULE =
+ new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+ .setOwner(new ComponentName("pkg", "cls"))
+ .build();
+
private final int mUid = Binder.getCallingUid();
private final @UserIdInt int mUserId = UserHandle.getUserId(mUid);
@@ -351,9 +367,6 @@
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
- @Rule
- public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
@@ -541,6 +554,7 @@
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
mContext.addMockSystemService(NotificationManager.class, mMockNm);
+ doNothing().when(mContext).sendBroadcast(any(), anyString());
doNothing().when(mContext).sendBroadcastAsUser(any(), any());
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
@@ -564,6 +578,9 @@
when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer(
(Answer<Boolean>) invocation -> {
+ // TODO: b/317957802 - This is overly broad and basically makes ANY
+ // isSameApp() check pass, requiring Mockito.reset() for meaningful
+ // tests! Make it more precise.
Object[] args = invocation.getArguments();
return (int) args[1] == mUid;
});
@@ -903,7 +920,9 @@
}
private ApplicationInfo getApplicationInfo(String pkg, int uid) {
final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = pkg;
applicationInfo.uid = uid;
+ applicationInfo.sourceDir = mContext.getApplicationInfo().sourceDir;
switch (pkg) {
case PKG_N_MR1:
applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -5529,15 +5548,6 @@
@Test
public void testBumpFGImportance_channelChangePreOApp() throws Exception {
- String preOPkg = PKG_N_MR1;
- final ApplicationInfo legacy = new ApplicationInfo();
- legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPackageManagerClient.getApplicationInfoAsUser(eq(preOPkg), anyInt(), anyInt()))
- .thenReturn(legacy);
- when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt()))
- .thenReturn(Binder.getCallingUid());
- getContext().setMockPackageManager(mPackageManagerClient);
-
Notification.Builder nb = new Notification.Builder(mContext,
NotificationChannel.DEFAULT_CHANNEL_ID)
.setContentTitle("foo")
@@ -5545,7 +5555,7 @@
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
- StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+ StatusBarNotification sbn = new StatusBarNotification(PKG_N_MR1, PKG_N_MR1, 9,
"testBumpFGImportance_channelChangePreOApp",
Binder.getCallingUid(), 0, nb.build(),
UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
@@ -5565,11 +5575,11 @@
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
- sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+ sbn = new StatusBarNotification(PKG_N_MR1, PKG_N_MR1, 9,
"testBumpFGImportance_channelChangePreOApp", Binder.getCallingUid(),
0, nb.build(), UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
- mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg,
+ mBinderService.enqueueNotificationWithTag(PKG_N_MR1, PKG_N_MR1,
"testBumpFGImportance_channelChangePreOApp",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -5577,7 +5587,7 @@
mService.getNotificationRecord(sbn.getKey()).getImportance());
NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
- preOPkg, mContext.getUserId(), preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
+ PKG_N_MR1, mContext.getUserId(), PKG_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID);
assertEquals(IMPORTANCE_LOW, defaultChannel.getImportance());
}
@@ -9048,7 +9058,7 @@
zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
try {
- mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName(), false);
fail("Zen policy only applies to priority only mode");
} catch (IllegalArgumentException e) {
// yay
@@ -9056,11 +9066,11 @@
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName(), false);
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
- mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName(), false);
}
@Test
@@ -9075,7 +9085,7 @@
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
+ mBinderService.addAutomaticZenRule(rule, "com.android.settings", false);
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
@@ -9097,7 +9107,7 @@
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "com.android.settings");
+ mBinderService.addAutomaticZenRule(rule, "com.android.settings", false);
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
@@ -9117,7 +9127,7 @@
boolean isEnabled = true;
AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule, "another.package");
+ mBinderService.addAutomaticZenRule(rule, "another.package", false);
// verify that zen mode helper gets passed in the package name from the arg, not the owner
verify(mockZenModeHelper).addAutomaticZenRule(
@@ -9128,37 +9138,246 @@
@Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeManagedCanBeUsedByDeviceOwners() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.setCallerIsNormalPackage();
- mService.setZenHelper(mock(ZenModeHelper.class));
- when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
- .thenReturn(true);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
.setType(AutomaticZenRule.TYPE_MANAGED)
- .setOwner(new ComponentName("pkg", "cls"))
+ .setOwner(new ComponentName(PKG, "cls"))
.build();
when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(true);
- mBinderService.addAutomaticZenRule(rule, "pkg");
- // No exception!
+ mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq(PKG), eq(rule), anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void testAddAutomaticZenRule_typeManagedCanBeUsedBySystem() throws Exception {
+ addAutomaticZenRule_restrictedRuleTypeCanBeUsedBySystem(AutomaticZenRule.TYPE_MANAGED);
}
@Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeManagedCannotBeUsedByRegularApps() throws Exception {
+ addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
+ AutomaticZenRule.TYPE_MANAGED);
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void testAddAutomaticZenRule_typeBedtimeCanBeUsedByWellbeing() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+ reset(mPackageManagerInternal);
+ when(mPackageManagerInternal.isSameApp(eq(PKG), eq(mUid), anyInt())).thenReturn(true);
+ when(mResources
+ .getString(com.android.internal.R.string.config_systemWellbeing))
+ .thenReturn(PKG);
+ when(mContext.getResources()).thenReturn(mResources);
+
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setOwner(new ComponentName(PKG, "cls"))
+ .build();
+
+ mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq(PKG), eq(rule), anyInt(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void testAddAutomaticZenRule_typeBedtimeCanBeUsedBySystem() throws Exception {
+ reset(mPackageManagerInternal);
+ when(mPackageManagerInternal.isSameApp(eq(PKG), eq(mUid), anyInt())).thenReturn(true);
+ addAutomaticZenRule_restrictedRuleTypeCanBeUsedBySystem(AutomaticZenRule.TYPE_BEDTIME);
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void testAddAutomaticZenRule_typeBedtimeCannotBeUsedByRegularApps() throws Exception {
+ reset(mPackageManagerInternal);
+ when(mPackageManagerInternal.isSameApp(eq(PKG), eq(mUid), anyInt())).thenReturn(true);
+ addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
+ AutomaticZenRule.TYPE_BEDTIME);
+ }
+
+ private void addAutomaticZenRule_restrictedRuleTypeCanBeUsedBySystem(
+ @AutomaticZenRule.Type int ruleType) throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
+ .setType(ruleType)
+ .setOwner(new ComponentName(PKG, "cls"))
+ .build();
+ when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(true);
+
+ mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq(PKG), eq(rule), anyInt(), any(), anyInt());
+ }
+
+ private void addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
+ @AutomaticZenRule.Type int ruleType) {
mService.setCallerIsNormalPackage();
mService.setZenHelper(mock(ZenModeHelper.class));
when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
.thenReturn(true);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri"))
- .setType(AutomaticZenRule.TYPE_MANAGED)
- .setOwner(new ComponentName("pkg", "cls"))
+ .setType(ruleType)
+ .setOwner(new ComponentName(PKG, "cls"))
.build();
when(mDevicePolicyManager.isActiveDeviceOwner(anyInt())).thenReturn(false);
assertThrows(IllegalArgumentException.class,
- () -> mBinderService.addAutomaticZenRule(rule, "pkg"));
+ () -> mBinderService.addAutomaticZenRule(rule, PKG, /* fromUser= */ false));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromUser_mappedToOriginUser() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ true);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromSystemNotUser_mappedToOriginSystem() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromApp_mappedToOriginApp() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ false);
+
+ verify(zenModeHelper).addAutomaticZenRule(eq("pkg"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void addAutomaticZenRule_fromAppFromUser_blocked() throws Exception {
+ setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ assertThrows(SecurityException.class, () ->
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ true));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void updateAutomaticZenRule_fromUserFromSystem_allowed() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.updateAutomaticZenRule("id", SOME_ZEN_RULE, /* fromUser= */ true);
+
+ verify(zenModeHelper).updateAutomaticZenRule(eq("id"), eq(SOME_ZEN_RULE),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void updateAutomaticZenRule_fromUserFromApp_blocked() throws Exception {
+ setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ assertThrows(SecurityException.class, () ->
+ mBinderService.addAutomaticZenRule(SOME_ZEN_RULE, "pkg", /* fromUser= */ true));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void removeAutomaticZenRule_fromUserFromSystem_allowed() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ mBinderService.removeAutomaticZenRule("id", /* fromUser= */ true);
+
+ verify(zenModeHelper).removeAutomaticZenRule(eq("id"),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyString(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void removeAutomaticZenRule_fromUserFromApp_blocked() throws Exception {
+ setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ assertThrows(SecurityException.class, () ->
+ mBinderService.removeAutomaticZenRule("id", /* fromUser= */ true));
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_conditionFromUser_mappedToOriginUser() throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ Condition withSourceUser = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+ SOURCE_USER_ACTION);
+ mBinderService.setAutomaticZenRuleState("id", withSourceUser);
+
+ verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceUser),
+ eq(ZenModeConfig.UPDATE_ORIGIN_USER), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_fromAppWithConditionNotFromUser_mappedToOriginApp()
+ throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.setCallerIsNormalPackage();
+
+ Condition withSourceContext = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+ SOURCE_CONTEXT);
+ mBinderService.setAutomaticZenRuleState("id", withSourceContext);
+
+ verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyInt());
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_fromSystemWithConditionNotFromUser_mappedToOriginSystem()
+ throws Exception {
+ ZenModeHelper zenModeHelper = setUpMockZenTest();
+ mService.isSystemUid = true;
+
+ Condition withSourceContext = new Condition(Uri.parse("uri"), "summary", STATE_TRUE,
+ SOURCE_CONTEXT);
+ mBinderService.setAutomaticZenRuleState("id", withSourceContext);
+
+ verify(zenModeHelper).setAutomaticZenRuleState(eq("id"), eq(withSourceContext),
+ eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyInt());
+ }
+
+ /** Prepares for a zen-related test that uses a mocked {@link ZenModeHelper}. */
+ private ZenModeHelper setUpMockZenTest() {
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.setZenHelper(zenModeHelper);
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ return zenModeHelper;
}
@Test
@@ -9184,7 +9403,7 @@
});
mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null,
- "testing!");
+ "testing!", false);
waitForIdle();
InOrder inOrder = inOrder(mContext);
@@ -11119,6 +11338,40 @@
}
@Test
+ public void testMigrateNotificationFilter_invalidPackage() throws Exception {
+ int[] userIds = new int[] {mUserId, 1000};
+ when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
+ List<String> disallowedApps = ImmutableList.of("apples", "bananas", "cherries");
+ for (int userId : userIds) {
+ when(mPackageManager.getPackageUid("apples", 0, userId)).thenThrow(
+ new RemoteException(""));
+ when(mPackageManager.getPackageUid("bananas", 0, userId)).thenReturn(9000);
+ when(mPackageManager.getPackageUid("cherries", 0, userId)).thenReturn(9001);
+ }
+
+ when(mListeners.getNotificationListenerFilter(any())).thenReturn(
+ new NotificationListenerFilter());
+
+ mBinderService.migrateNotificationFilter(null,
+ FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ONGOING,
+ disallowedApps);
+
+ ArgumentCaptor<NotificationListenerFilter> captor =
+ ArgumentCaptor.forClass(NotificationListenerFilter.class);
+ verify(mListeners).setNotificationListenerFilter(any(), captor.capture());
+
+ assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ONGOING,
+ captor.getValue().getTypes());
+ // valid values stay
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("bananas", 9000)));
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("cherries", 9001)));
+ // don't store invalid values
+ for (VersionedPackage vp : captor.getValue().getDisallowedPackages()) {
+ assertNotEquals("apples", vp.getPackageName());
+ }
+ }
+
+ @Test
public void testMigrateNotificationFilter_noPreexistingFilter() throws Exception {
int[] userIds = new int[] {mUserId};
when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
@@ -11632,8 +11885,8 @@
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testGetActiveNotificationsFromListener_redactNotification() throws Exception {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
NotificationRecord r =
generateNotificationRecord(mTestNotificationChannel, 0, 0);
mService.addNotification(r);
@@ -11662,12 +11915,11 @@
}
@Test
- @RequiresFlagsEnabled(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
public void testGetSnoozedNotificationsFromListener_redactNotification() throws Exception {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
NotificationRecord r =
generateNotificationRecord(mTestNotificationChannel, 0, 0);
- mService.addNotification(r);
- mService.snoozeNotificationInt(r.getKey(), 1000, null, mListener);
+ when(mSnoozeHelper.getSnoozed()).thenReturn(List.of(r));
when(mListeners.isUidTrusted(anyInt())).thenReturn(false);
when(mListeners.hasSensitiveContent(any())).thenReturn(true);
StatusBarNotification redacted = generateRedactedSbn(mTestNotificationChannel, 1, 1);
@@ -11847,6 +12099,97 @@
}
@Test
+ public void testMakeRankingUpdate_redactsIfRecordSensitiveAndServiceUntrusted() {
+ mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
+ when(mListeners.isUidTrusted(anyInt())).thenReturn(false);
+ when(mListeners.hasSensitiveContent(any())).thenReturn(true);
+ NotificationRecord pkgA = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+ addSmartActionsAndReplies(pkgA);
+ mService.addNotification(pkgA);
+ NotificationRecord pkgB = new NotificationRecord(mContext,
+ generateSbn("b", 1001, 9, 0), mTestNotificationChannel);
+ addSmartActionsAndReplies(pkgB);
+ mService.addNotification(pkgB);
+
+ ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+ when(info.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(info.isSameUser(anyInt())).thenReturn(true);
+ NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info);
+ NotificationListenerService.Ranking ranking =
+ nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+ assertEquals(0, ranking.getSmartActions().size());
+ assertEquals(0, ranking.getSmartReplies().size());
+ NotificationListenerService.Ranking ranking2 =
+ nru.getRankingMap().getRawRankingObject(pkgB.getSbn().getKey());
+ assertEquals(0, ranking2.getSmartActions().size());
+ assertEquals(0, ranking2.getSmartReplies().size());
+ }
+
+ @Test
+ public void testMakeRankingUpdate_doestntRedactIfFlagDisabled() {
+ mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
+ when(mListeners.isUidTrusted(anyInt())).thenReturn(false);
+ when(mListeners.hasSensitiveContent(any())).thenReturn(true);
+ NotificationRecord pkgA = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+ addSmartActionsAndReplies(pkgA);
+
+ mService.addNotification(pkgA);
+ ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+ when(info.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(info.isSameUser(anyInt())).thenReturn(true);
+ NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info);
+ NotificationListenerService.Ranking ranking =
+ nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+ assertEquals(1, ranking.getSmartActions().size());
+ assertEquals(1, ranking.getSmartReplies().size());
+ }
+
+ @Test
+ public void testMakeRankingUpdate_doesntRedactIfNotSensitiveOrServiceTrusted() {
+ mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
+ NotificationRecord pkgA = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+ addSmartActionsAndReplies(pkgA);
+
+ mService.addNotification(pkgA);
+ ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+ when(info.enabledAndUserMatches(anyInt())).thenReturn(true);
+ when(info.isSameUser(anyInt())).thenReturn(true);
+
+ // No sensitive content, no redaction
+ when(mListeners.isUidTrusted(eq(1000))).thenReturn(false);
+ when(mListeners.hasSensitiveContent(any())).thenReturn(false);
+ NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info);
+ NotificationListenerService.Ranking ranking =
+ nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+ assertEquals(1, ranking.getSmartActions().size());
+ assertEquals(1, ranking.getSmartReplies().size());
+
+ // trusted listener, no redaction
+ when(mListeners.isUidTrusted(eq(1000))).thenReturn(true);
+ when(mListeners.hasSensitiveContent(any())).thenReturn(true);
+ nru = mService.makeRankingUpdateLocked(info);
+ ranking = nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey());
+ assertEquals(1, ranking.getSmartActions().size());
+ assertEquals(1, ranking.getSmartReplies().size());
+ }
+
+ private void addSmartActionsAndReplies(NotificationRecord record) {
+ Bundle b = new Bundle();
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ actions.add(new Notification.Action(0, "", null));
+ b.putParcelableArrayList(KEY_CONTEXTUAL_ACTIONS, actions);
+ ArrayList<CharSequence> replies = new ArrayList<>(List.of("test"));
+ b.putCharSequenceArrayList(KEY_TEXT_REPLIES, replies);
+ Adjustment a = new Adjustment(record.getSbn().getPackageName(), record.getSbn().getKey(),
+ b, "", record.getUserId());
+ record.addAdjustment(a);
+ record.applyAdjustments();
+ }
+
+ @Test
public void testMaybeShowReviewPermissionsNotification_flagOff() {
mService.setShowReviewPermissionsNotification(false);
reset(mMockNm);
@@ -12157,7 +12500,9 @@
/* isImageBitmap= */ true,
/* isExpired= */ true);
addRecordAndRemoveBitmaps(record);
- assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE)).isFalse();
+ assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE)).isTrue();
+ final Parcelable picture = record.getNotification().extras.getParcelable(EXTRA_PICTURE);
+ assertThat(picture).isNull();
}
@Test
@@ -12191,7 +12536,10 @@
/* isImageBitmap= */ false,
/* isExpired= */ true);
addRecordAndRemoveBitmaps(record);
- assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
+ assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
+ final Parcelable pictureIcon =
+ record.getNotification().extras.getParcelable(EXTRA_PICTURE_ICON);
+ assertThat(pictureIcon).isNull();
}
@Test
@@ -13441,9 +13789,10 @@
.thenReturn(true);
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
- verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy));
+ verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP));
}
@Test
@@ -13457,14 +13806,38 @@
mService.isSystemUid = true;
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
- public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy()
+ throws RemoteException {
+ setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ AssociationRequest.DEVICE_PROFILE_WATCH, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_autoCompanionApp_setsGlobalPolicy()
+ throws RemoteException {
+ setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_otherCompanionApp_doesNotSetGlobalPolicy()
+ throws RemoteException {
+ setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false);
+ }
+
+ private void setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
+ @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
+ throws RemoteException {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
@@ -13474,14 +13847,19 @@
when(mCompanionMgr.getAssociations(anyString(), anyInt()))
.thenReturn(ImmutableList.of(
new AssociationInfo.Builder(1, mUserId, "package")
- .setDisplayName("My watch")
- .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .setDisplayName("My connected device")
+ .setDeviceProfile(deviceProfile)
.build()));
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
- verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+ if (canSetGlobalPolicy) {
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
+ } else {
+ verify(zenModeHelper).applyGlobalPolicyAsImplicitZenRule(anyString(), anyInt(),
+ eq(policy), anyInt());
+ }
}
@Test
@@ -13495,7 +13873,7 @@
.thenReturn(true);
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
- mBinderService.setNotificationPolicy("package", policy);
+ mBinderService.setNotificationPolicy("package", policy, false);
verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@@ -13525,7 +13903,7 @@
when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
.thenReturn(true);
- mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
verify(zenHelper).applyGlobalZenModeAsImplicitZenRule(eq("package"), anyInt(),
eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
@@ -13542,7 +13920,7 @@
.thenReturn(true);
mService.isSystemUid = true;
- mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), eq("package"),
@@ -13551,7 +13929,29 @@
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
- public void setInterruptionFilter_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ public void setInterruptionFilter_watchCompanionApp_setsGlobalZen() throws RemoteException {
+ setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ AssociationRequest.DEVICE_PROFILE_WATCH, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_autoCompanionApp_setsGlobalZen() throws RemoteException {
+ setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, true);
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_otherCompanionApp_doesNotSetGlobalZen()
+ throws RemoteException {
+ setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, false);
+ }
+
+ private void setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
+ @AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
+ throws RemoteException {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
@@ -13561,14 +13961,89 @@
when(mCompanionMgr.getAssociations(anyString(), anyInt()))
.thenReturn(ImmutableList.of(
new AssociationInfo.Builder(1, mUserId, "package")
- .setDisplayName("My watch")
- .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .setDisplayName("My connected device")
+ .setDeviceProfile(deviceProfile)
.build()));
- mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY, false);
- verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
- eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
+ if (canSetGlobalPolicy) {
+ verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
+ } else {
+ verify(zenModeHelper).applyGlobalZenModeAsImplicitZenRule(anyString(), anyInt(),
+ eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+ }
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void updateAutomaticZenRule_implicitRuleWithoutCPS_disallowedFromApp() throws Exception {
+ setUpRealZenTest();
+ mService.setCallerIsNormalPackage();
+ assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+
+ // Create an implicit zen rule by calling setNotificationPolicy from an app.
+ mBinderService.setNotificationPolicy(PKG, new NotificationManager.Policy(0, 0, 0), false);
+ assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
+ Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
+ mBinderService.getAutomaticZenRules().entrySet());
+ assertThat(rule.getValue().getOwner()).isNull();
+ assertThat(rule.getValue().getConfigurationActivity()).isNull();
+
+ // Now try to update said rule (e.g. disable it). Should fail.
+ // We also validate the exception message because NPE could be thrown by all sorts of test
+ // issues (e.g. misconfigured mocks).
+ rule.getValue().setEnabled(false);
+ NullPointerException e = assertThrows(NullPointerException.class,
+ () -> mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false));
+ assertThat(e.getMessage()).isEqualTo(
+ "Rule must have a ConditionProviderService and/or configuration activity");
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void updateAutomaticZenRule_implicitRuleWithoutCPS_allowedFromSystem() throws Exception {
+ setUpRealZenTest();
+ mService.setCallerIsNormalPackage();
+ assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+
+ // Create an implicit zen rule by calling setNotificationPolicy from an app.
+ mBinderService.setNotificationPolicy(PKG, new NotificationManager.Policy(0, 0, 0), false);
+ assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
+ Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
+ mBinderService.getAutomaticZenRules().entrySet());
+ assertThat(rule.getValue().getOwner()).isNull();
+ assertThat(rule.getValue().getConfigurationActivity()).isNull();
+
+ // Now update said rule from Settings (e.g. disable it). Should work!
+ mService.isSystemUid = true;
+ rule.getValue().setEnabled(false);
+ mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false);
+
+ Map.Entry<String, AutomaticZenRule> updatedRule = getOnlyElement(
+ mBinderService.getAutomaticZenRules().entrySet());
+ assertThat(updatedRule.getValue().isEnabled()).isFalse();
+ }
+
+ /** Prepares for a zen-related test that uses the real {@link ZenModeHelper}. */
+ private void setUpRealZenTest() throws Exception {
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ int iconResId = 79;
+ String iconResName = "icon_79";
+ String pkg = mContext.getPackageName();
+ ApplicationInfo appInfoSpy = spy(new ApplicationInfo());
+ appInfoSpy.icon = iconResId;
+ when(appInfoSpy.loadLabel(any())).thenReturn("Test App");
+ when(mPackageManagerClient.getApplicationInfo(eq(pkg), anyInt())).thenReturn(appInfoSpy);
+
+ when(mResources.getResourceName(eq(iconResId))).thenReturn(iconResName);
+ when(mResources.getIdentifier(eq(iconResName), any(), any())).thenReturn(iconResId);
+ when(mPackageManagerClient.getResourcesForApplication(eq(pkg))).thenReturn(mResources);
}
@Test
@@ -13586,6 +14061,7 @@
}
@Test
+ @Ignore("b/316989461")
public void cancelNotificationsFromListener_rapidClear_oldNew_cancelOne()
throws RemoteException {
mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
@@ -13615,6 +14091,7 @@
}
@Test
+ @Ignore("b/316989461")
public void cancelNotificationsFromListener_rapidClear_old_cancelOne() throws RemoteException {
mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
@@ -13642,6 +14119,7 @@
}
@Test
+ @Ignore("b/316989461")
public void cancelNotificationsFromListener_rapidClear_oldNew_cancelOne_flagDisabled()
throws RemoteException {
mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
@@ -13672,6 +14150,7 @@
}
@Test
+ @Ignore("b/316989461")
public void cancelNotificationsFromListener_rapidClear_oldNew_cancelAll()
throws RemoteException {
mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
@@ -13700,6 +14179,7 @@
}
@Test
+ @Ignore("b/316989461")
public void cancelNotificationsFromListener_rapidClear_old_cancelAll() throws RemoteException {
mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags
.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED);
@@ -13726,6 +14206,7 @@
}
@Test
+ @Ignore("b/316989461")
public void cancelNotificationsFromListener_rapidClear_oldNew_cancelAll_flagDisabled()
throws RemoteException {
mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags
@@ -13754,6 +14235,22 @@
any(), any());
}
+ @Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testSetPrivateNotificationsAllowed() throws Exception {
+ when(mContext.checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS))
+ .thenReturn(PERMISSION_GRANTED);
+ mBinderService.setPrivateNotificationsAllowed(false);
+ Intent expected = new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+ ArgumentCaptor<Intent> actual = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcast(actual.capture(), eq(STATUS_BAR_SERVICE));
+
+ assertEquals(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED, actual.getValue().getAction());
+ assertFalse(actual.getValue().getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
+ assertFalse(mBinderService.getPrivateNotificationsAllowed());
+ }
+
private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
throws RemoteException {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
index 4a1435f..5b35e34 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
@@ -99,7 +99,7 @@
public boolean getFromSystemOrSystemUi(int i) throws IllegalArgumentException {
// While this isn't a logged output value, it's still helpful to check in tests.
checkInRange(i);
- return mChanges.get(i).mFromSystemOrSystemUi;
+ return mChanges.get(i).isFromSystemOrSystemUi();
}
public boolean getIsUserAction(int i) throws IllegalArgumentException {
@@ -118,10 +118,13 @@
public DNDPolicyProto getPolicyProto(int i) throws IllegalArgumentException {
checkInRange(i);
byte[] policyBytes = mChanges.get(i).getDNDPolicyProto();
+ if (policyBytes == null) {
+ return null;
+ }
try {
return DNDPolicyProto.parseFrom(policyBytes);
} catch (InvalidProtocolBufferException e) {
- return null; // couldn't turn it into proto
+ throw new RuntimeException("Couldn't parse DNDPolicyProto!", e);
}
}
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 1aea56c..25c0cd9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -43,6 +43,8 @@
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
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_SCHEDULE;
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP;
@@ -112,6 +114,7 @@
import android.os.Parcel;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -119,12 +122,13 @@
import android.service.notification.DeviceEffectsApplier;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeDiff;
import android.service.notification.ZenPolicy;
import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
+import android.testing.TestWithLooperRule;
import android.testing.TestableLooper;
import android.util.ArrayMap;
import android.util.Log;
@@ -147,6 +151,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.truth.Correspondence;
import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import org.junit.Before;
import org.junit.Rule;
@@ -172,7 +178,7 @@
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
-@RunWith(AndroidTestingRunner.class)
+@RunWith(TestParameterInjector.class)
@TestableLooper.RunWithLooper
public class ZenModeHelperTest extends UiServiceTestCase {
@@ -211,7 +217,11 @@
private static final ZenDeviceEffects NO_EFFECTS = new ZenDeviceEffects.Builder().build();
@Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+ SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+
+ @Rule(order = Integer.MAX_VALUE) // set the highest order so it's the innermost rule
+ public TestWithLooperRule mLooperRule = new TestWithLooperRule();
ConditionProviders mConditionProviders;
@Mock NotificationManager mNotificationManager;
@@ -2339,15 +2349,38 @@
assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
}
+ private enum ModesApiFlag {
+ ENABLED(true, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER),
+ DISABLED(false, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI);
+
+ private final boolean mEnabled;
+ @ConfigChangeOrigin private final int mOriginForUserActionInSystemUi;
+
+ ModesApiFlag(boolean enabled, @ConfigChangeOrigin int originForUserActionInSystemUi) {
+ this.mEnabled = enabled;
+ this.mOriginForUserActionInSystemUi = originForUserActionInSystemUi;
+ }
+
+ void applyFlag(SetFlagsRule setFlagsRule) {
+ if (mEnabled) {
+ setFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ } else {
+ setFlagsRule.disableFlags(Flags.FLAG_MODES_API);
+ }
+ }
+ }
+
@Test
- public void testZenModeEventLog_setManualZenMode() throws IllegalArgumentException {
+ public void testZenModeEventLog_setManualZenMode(@TestParameter ModesApiFlag modesApiFlag)
+ throws IllegalArgumentException {
+ modesApiFlag.applyFlag(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Turn zen mode on (to important_interruptions)
// Need to additionally call the looper in order to finish the post-apply-config process
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID);
+ modesApiFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID);
// Now turn zen mode off, but via a different package UID -- this should get registered as
// "not an action by the user" because some other app is changing zen mode
@@ -2374,7 +2407,8 @@
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
- assertTrue(mZenModeEventLogger.getFromSystemOrSystemUi(0));
+ assertThat(mZenModeEventLogger.getFromSystemOrSystemUi(0)).isEqualTo(
+ modesApiFlag == ModesApiFlag.DISABLED);
assertTrue(mZenModeEventLogger.getIsUserAction(0));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
@@ -2395,11 +2429,17 @@
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
- checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+ if (Flags.modesApi()) {
+ assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
+ } else {
+ checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+ }
}
@Test
- public void testZenModeEventLog_automaticRules() throws IllegalArgumentException {
+ public void testZenModeEventLog_automaticRules(@TestParameter ModesApiFlag modesApiFlag)
+ throws IllegalArgumentException {
+ modesApiFlag.applyFlag(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -2421,8 +2461,8 @@
// Event 2: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
- Process.SYSTEM_UID);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule,
+ modesApiFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID);
// Add a new system rule
AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
@@ -2440,8 +2480,8 @@
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Event 4: "User" deletes the rule
- mZenModeHelper.removeAutomaticZenRule(systemId, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
- Process.SYSTEM_UID);
+ mZenModeHelper.removeAutomaticZenRule(systemId, modesApiFlag.mOriginForUserActionInSystemUi,
+ "", Process.SYSTEM_UID);
// In total, this represents 4 events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -2475,7 +2515,11 @@
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertTrue(mZenModeEventLogger.getIsUserAction(1));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
- checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+ if (Flags.modesApi()) {
+ assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
+ } else {
+ checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
+ }
// When the system rule is enabled, this counts as an automatic action that comes from the
// system and turns on DND
@@ -2497,20 +2541,109 @@
}
@Test
- public void testZenModeEventLog_policyChanges() throws IllegalArgumentException {
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void testZenModeEventLog_automaticRuleActivatedFromAppByAppAndUser()
+ throws IllegalArgumentException {
+ mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
+ setupZenConfig();
+
+ // Ann app adds an automatic zen rule
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
+
+ // Event 1: Mimic the rule coming on manually when the user turns it on in the app
+ // ("Turn on bedtime now" because user goes to bed earlier).
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_USER_ACTION),
+ UPDATE_ORIGIN_USER, CUSTOM_PKG_UID);
+
+ // Event 2: App deactivates the rule automatically (it's 8 AM, bedtime schedule ends)
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ // Event 3: App activates the rule automatically (it's now 11 PM, bedtime schedule starts)
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ // Event 4: User deactivates the rule manually (they get up before 8 AM on the next day)
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_USER_ACTION),
+ UPDATE_ORIGIN_USER, CUSTOM_PKG_UID);
+
+ // In total, this represents 4 events
+ assertEquals(4, mZenModeEventLogger.numLoggedChanges());
+
+ // Automatic rule turning on manually:
+ // - event ID: DND_TURNED_ON
+ // - 1 rule (newly) active
+ // - is a user action
+ // - package UID is the calling package
+ assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
+ mZenModeEventLogger.getEventId(0));
+ assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
+ assertTrue(mZenModeEventLogger.getIsUserAction(0));
+ assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
+
+ // Automatic rule turned off automatically by app:
+ // - event ID: DND_TURNED_OFF
+ // - 0 rules active
+ // - is not a user action
+ // - package UID is the calling package
+ assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
+ mZenModeEventLogger.getEventId(1));
+ assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
+ assertFalse(mZenModeEventLogger.getIsUserAction(1));
+ assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
+
+ // Automatic rule turned on automatically by app:
+ // - event ID: DND_TURNED_ON
+ // - 1 rule (newly) active
+ // - is not a user action
+ // - package UID is the calling package
+ assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
+ mZenModeEventLogger.getEventId(2));
+ assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(2));
+ assertEquals(1, mZenModeEventLogger.getNumRulesActive(2));
+ assertFalse(mZenModeEventLogger.getIsUserAction(2));
+ assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(2));
+
+ // Automatic rule turned off automatically by the user:
+ // - event ID: DND_TURNED_ON
+ // - 0 rules active
+ // - is a user action
+ // - package UID is the calling package
+ assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
+ mZenModeEventLogger.getEventId(3));
+ assertEquals(0, mZenModeEventLogger.getNumRulesActive(3));
+ assertTrue(mZenModeEventLogger.getIsUserAction(3));
+ assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(3));
+ }
+
+ @Test
+ public void testZenModeEventLog_policyChanges(@TestParameter ModesApiFlag modesApiFlag)
+ throws IllegalArgumentException {
+ modesApiFlag.applyFlag(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// First just turn zen mode on
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID);
+ modesApiFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID);
// Now change the policy slightly; want to confirm that this'll be reflected in the logs
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.allowAlarms = true;
newConfig.allowRepeatCallers = false;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ modesApiFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID);
// Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
// is off.
@@ -2521,7 +2654,7 @@
newConfig.allowMessages = false;
newConfig.allowRepeatCallers = true;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ modesApiFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID);
// Total events: we only expect ones for turning on, changing policy, and turning off
assertEquals(3, mZenModeEventLogger.numLoggedChanges());
@@ -2554,7 +2687,9 @@
}
@Test
- public void testZenModeEventLog_ruleCounts() throws IllegalArgumentException {
+ public void testZenModeEventLog_ruleCounts(@TestParameter ModesApiFlag modesApiFlag)
+ throws IllegalArgumentException {
+ modesApiFlag.applyFlag(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -2657,8 +2792,10 @@
}
@Test
- public void testZenModeEventLog_noLogWithNoConfigChange() throws IllegalArgumentException {
+ public void testZenModeEventLog_noLogWithNoConfigChange(
+ @TestParameter ModesApiFlag modesApiFlag) throws IllegalArgumentException {
// If evaluateZenMode is called independently of a config change, don't log.
+ modesApiFlag.applyFlag(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -2675,9 +2812,11 @@
}
@Test
- public void testZenModeEventLog_reassignUid() throws IllegalArgumentException {
+ public void testZenModeEventLog_reassignUid(@TestParameter ModesApiFlag modesApiFlag)
+ throws IllegalArgumentException {
// Test that, only in specific cases, we reassign the calling UID to one associated with
// the automatic rule owner.
+ modesApiFlag.applyFlag(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -2689,7 +2828,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
+ UPDATE_ORIGIN_APP, "test", Process.SYSTEM_UID);
// Rule 2, same as rule 1 but owned by the system
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -2699,11 +2838,11 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
+ modesApiFlag.mOriginForUserActionInSystemUi, "test", Process.SYSTEM_UID);
// Turn on rule 1; call looks like it's from the system. Because setting a condition is
// typically an automatic (non-user-initiated) action, expect the calling UID to be
- // re-evaluated to the one associat.d with CUSTOM_PKG_NAME.
+ // re-evaluated to the one associated with CUSTOM_PKG_NAME.
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
@@ -2717,8 +2856,8 @@
// Disable rule 1. Because this looks like a user action, the UID should not be modified
// from the system-provided one.
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
- Process.SYSTEM_UID);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule,
+ modesApiFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID);
// Add a manual rule. Any manual rule changes should not get calling uids reassigned.
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
@@ -2775,8 +2914,10 @@
}
@Test
- public void testZenModeEventLog_channelsBypassingChanges() {
+ public void testZenModeEventLog_channelsBypassingChanges(
+ @TestParameter ModesApiFlag modesApiFlag) {
// Verify that the right thing happens when the canBypassDnd value changes.
+ modesApiFlag.applyFlag(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -2872,20 +3013,59 @@
// Second message where we change the policy:
// - DND_POLICY_CHANGED (indicates only the policy changed and nothing else)
// - rule type: unknown (it's a policy change, not a rule change)
- // - user action (because it comes from a "system" uid)
// - change is in allow channels, and final policy
assertThat(mZenModeEventLogger.getEventId(1))
.isEqualTo(ZenModeEventLogger.ZenStateChangedEvent.DND_POLICY_CHANGED.getId());
assertThat(mZenModeEventLogger.getChangedRuleType(1))
.isEqualTo(DNDProtoEnums.UNKNOWN_RULE);
- assertThat(mZenModeEventLogger.getIsUserAction(1)).isTrue();
- assertThat(mZenModeEventLogger.getPackageUid(1)).isEqualTo(Process.SYSTEM_UID);
DNDPolicyProto dndProto = mZenModeEventLogger.getPolicyProto(1);
assertThat(dndProto.getAllowChannels().getNumber())
.isEqualTo(DNDProtoEnums.CHANNEL_TYPE_NONE);
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() {
+ mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
+ setupZenConfig();
+
+ // An app adds an automatic zen rule
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "cls"),
+ Uri.parse("condition"),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_ALL, true);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
+
+ // Event 1: App activates the rule automatically.
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ // Event 2: App deactivates the rule automatically.
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ // In total, this represents 2 events.
+ assertEquals(2, mZenModeEventLogger.numLoggedChanges());
+
+ // However, they are not DND_TURNED_ON/_OFF (no notification filtering is taking place).
+ // Also, no consolidated ZenPolicy is logged (because of the same reason).
+ assertThat(mZenModeEventLogger.getEventId(0)).isEqualTo(
+ ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId());
+ assertThat(mZenModeEventLogger.getNumRulesActive(0)).isEqualTo(1);
+ assertThat(mZenModeEventLogger.getPolicyProto(0)).isNull();
+
+ assertThat(mZenModeEventLogger.getEventId(1)).isEqualTo(
+ ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId());
+ assertThat(mZenModeEventLogger.getNumRulesActive(1)).isEqualTo(0);
+ assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
+ }
+
+ @Test
public void testUpdateConsolidatedPolicy_defaultRulesOnly() {
setupZenConfig();
@@ -3073,6 +3253,52 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() {
+ setupZenConfig();
+
+ // Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy.
+ // Note: rules with filter != PRIORITY should not have a custom policy. However, as of V
+ // this is only validated on rule addition, but not on rule update. :/
+
+ // Rule 1: PRIORITY, custom policy but not very strict (in fact, less strict than default).
+ AutomaticZenRule zenRuleWithPriority = new AutomaticZenRule("Priority",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "cls"),
+ Uri.parse("priority"),
+ new ZenPolicy.Builder().allowMedia(true).build(),
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String rule1Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRuleWithPriority, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
+ mZenModeHelper.setAutomaticZenRuleState(rule1Id,
+ new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ // Rule 2: ALL, but somehow with a super strict ZenPolicy.
+ AutomaticZenRule zenRuleWithAll = new AutomaticZenRule("All",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "cls"),
+ Uri.parse("priority"),
+ new ZenPolicy.Builder().disallowAllSounds().build(),
+ NotificationManager.INTERRUPTION_FILTER_ALL, true);
+ String rule2Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRuleWithAll, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
+ mZenModeHelper.setAutomaticZenRuleState(rule2Id,
+ new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ // Consolidated Policy should be default + rule1.
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse(); // default
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse(); // default
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isTrue(); // default
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue(); // default
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default
+ assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isTrue(); // default
+ }
+
+ @Test
public void zenRuleToAutomaticZenRule_allFields() {
mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
@@ -3372,6 +3598,29 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() {
+ mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
+ reset(mDeviceEffectsApplier);
+
+ String ruleId = addRuleWithEffects(new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .build());
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ mTestableLooper.processAllMessages();
+ verify(mDeviceEffectsApplier).apply(any(), eq(UPDATE_ORIGIN_APP));
+
+ // Now delete the (currently active!) rule. For example, assume this is done from settings.
+ mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_USER, "remove",
+ Process.SYSTEM_UID);
+ mTestableLooper.processAllMessages();
+
+ verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_USER));
+ }
+
+ @Test
public void testDeviceEffects_applied() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
@@ -3511,8 +3760,8 @@
AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
.setDeviceEffects(effects)
.build();
- return mZenModeHelper.addAutomaticZenRule("pkg", rule, UPDATE_ORIGIN_APP, "",
- CUSTOM_PKG_UID);
+ return mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+ UPDATE_ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
}
@Test
@@ -3619,7 +3868,8 @@
Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
- mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy,
+ UPDATE_ORIGIN_APP);
ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
.disallowAllSounds()
@@ -3643,13 +3893,14 @@
PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
- original);
+ original, UPDATE_ORIGIN_APP);
// Change priorityCallSenders: contacts -> starred.
Policy updated = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
PRIORITY_SENDERS_STARRED, PRIORITY_SENDERS_STARRED,
Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
- mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated,
+ UPDATE_ORIGIN_APP);
ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
.disallowAllSounds()
@@ -3671,7 +3922,7 @@
withoutWtfCrash(
() -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME,
- CUSTOM_PKG_UID, new Policy(0, 0, 0)));
+ CUSTOM_PKG_UID, new Policy(0, 0, 0), UPDATE_ORIGIN_APP));
assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
}
@@ -3684,7 +3935,7 @@
Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
CONVERSATION_SENDERS_IMPORTANT);
mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
- writtenPolicy);
+ writtenPolicy, UPDATE_ORIGIN_APP);
Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
CUSTOM_PKG_NAME);
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 6f37967..66dcaff 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -31,13 +31,13 @@
"frameworks-base-testutils",
"frameworks-services-vibrator-testutils",
"junit",
- "mockito-target-minus-junit4",
+ "mockito-target-inline-minus-junit4",
"platform-test-annotations",
"service-permission.stubs.system_server",
"services.core",
"flag-junit",
],
-
+ jni_libs: ["libdexmakerjvmtiagent"],
platform_apis: true,
certificate: "platform",
dxflags: ["--multi-dex"],
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index bbca704e..f9fe6a9 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -43,12 +43,17 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.test.TestLooper;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
+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 android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -68,6 +73,9 @@
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock private PowerManagerInternal mPowerManagerInternalMock;
@Mock private PackageManagerInternal mPackageManagerInternalMock;
@@ -256,6 +264,29 @@
assertEquals(0.5, scaled.getScale(), 1e-5);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void scale_withAdaptiveHaptics_scalesVibrationsCorrectly() {
+ setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
+ setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
+
+ SparseArray<Float> adaptiveHapticsScales = new SparseArray<>();
+ adaptiveHapticsScales.put(USAGE_RINGTONE, 0.5f);
+ adaptiveHapticsScales.put(USAGE_NOTIFICATION, 0.5f);
+ mVibrationScaler.updateAdaptiveHapticsScales(adaptiveHapticsScales);
+
+ StepSegment scaled = getFirstSegment(mVibrationScaler.scale(
+ VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE));
+ // Ringtone scales down.
+ assertTrue(scaled.getAmplitude() < 0.5);
+
+ scaled = getFirstSegment(mVibrationScaler.scale(
+ VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1),
+ USAGE_NOTIFICATION));
+ // Notification scales down.
+ assertTrue(scaled.getAmplitude() < 0.5);
+ }
+
private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
@Vibrator.VibrationIntensity int intensity) {
when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 49efd1b..1e0b1df 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -16,21 +16,49 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.frameworks.vibrator.ScaleParam;
+import android.frameworks.vibrator.VibrationParam;
import android.os.RemoteException;
+import android.util.SparseArray;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
public class VibratorControlServiceTest {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ private VibrationScaler mMockVibrationScaler;
+ @Captor
+ private ArgumentCaptor<SparseArray<Float>> mVibrationScalesCaptor;
+
private VibratorControlService mVibratorControlService;
private final Object mLock = new Object();
@Before
public void setUp() throws Exception {
- mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock);
+ mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(),
+ mMockVibrationScaler, mLock);
}
@Test
@@ -47,6 +75,8 @@
FakeVibratorController fakeController = new FakeVibratorController();
mVibratorControlService.registerVibratorController(fakeController);
mVibratorControlService.unregisterVibratorController(fakeController);
+
+ verify(mMockVibrationScaler).updateAdaptiveHapticsScales(null);
assertThat(fakeController.isLinkedToDeath).isFalse();
}
@@ -56,8 +86,91 @@
FakeVibratorController fakeController1 = new FakeVibratorController();
FakeVibratorController fakeController2 = new FakeVibratorController();
mVibratorControlService.registerVibratorController(fakeController1);
-
mVibratorControlService.unregisterVibratorController(fakeController2);
+
+ verifyZeroInteractions(mMockVibrationScaler);
assertThat(fakeController1.isLinkedToDeath).isTrue();
}
+
+ @Test
+ public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly()
+ throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+ SparseArray<Float> vibrationScales = new SparseArray<>();
+ vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+ vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+ mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+ fakeController);
+
+ verify(mMockVibrationScaler).updateAdaptiveHapticsScales(mVibrationScalesCaptor.capture());
+ SparseArray<Float> cachedVibrationScales = mVibrationScalesCaptor.getValue();
+ assertThat(cachedVibrationScales.size()).isEqualTo(3);
+ assertThat(cachedVibrationScales.keyAt(0)).isEqualTo(USAGE_ALARM);
+ assertThat(cachedVibrationScales.valueAt(0)).isEqualTo(0.7f);
+ assertThat(cachedVibrationScales.keyAt(1)).isEqualTo(USAGE_NOTIFICATION);
+ assertThat(cachedVibrationScales.valueAt(1)).isEqualTo(0.4f);
+ // Setting ScaleParam.TYPE_NOTIFICATION will update vibration scaling for both
+ // notification and communication request usages.
+ assertThat(cachedVibrationScales.keyAt(2)).isEqualTo(USAGE_COMMUNICATION_REQUEST);
+ assertThat(cachedVibrationScales.valueAt(2)).isEqualTo(0.4f);
+ }
+
+ @Test
+ public void testSetVibrationParams_withUnregisteredController_ignoresRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+
+ SparseArray<Float> vibrationScales = new SparseArray<>();
+ vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+ vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+ mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+ fakeController);
+
+ verifyZeroInteractions(mMockVibrationScaler);
+ }
+
+ @Test
+ public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales()
+ throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+ mVibratorControlService.registerVibratorController(fakeController);
+ mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM, fakeController);
+
+ verify(mMockVibrationScaler).updateAdaptiveHapticsScales(null);
+ }
+
+ @Test
+ public void testClearVibrationParams_withUnregisteredController_ignoresRequest()
+ throws RemoteException {
+ FakeVibratorController fakeController = new FakeVibratorController();
+
+ mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM, fakeController);
+
+ verifyZeroInteractions(mMockVibrationScaler);
+ }
+
+ private VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
+ List<VibrationParam> vibrationParamList = new ArrayList<>();
+ for (int i = 0; i < vibrationScales.size(); i++) {
+ int type = vibrationScales.keyAt(i);
+ float scale = vibrationScales.valueAt(i);
+
+ vibrationParamList.add(generateVibrationParam(type, scale));
+ }
+
+ return vibrationParamList.toArray(new VibrationParam[0]);
+ }
+
+ private VibrationParam generateVibrationParam(int type, float scale) {
+ ScaleParam scaleParam = new ScaleParam();
+ scaleParam.typesMask = type;
+ scaleParam.scale = scale;
+ VibrationParam vibrationParam = new VibrationParam();
+ vibrationParam.setScale(scaleParam);
+
+ return vibrationParam;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
index d2ef180..ca3787e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java
@@ -95,6 +95,26 @@
assertFalse(action.executed);
}
+ @Test
+ public void queueKeyAction_beforeAndAfterCancelQueuedActions_onlyActionsAfterCancelExecuted() {
+ TestAction action1 = new TestAction();
+ TestAction action2 = new TestAction();
+ TestAction action3 = new TestAction();
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action1);
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action2);
+ mKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY);
+ mKeyActionExecutor.queueKeyAction(
+ KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action3);
+
+ mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1);
+
+ assertFalse(action1.executed);
+ assertFalse(action2.executed);
+ assertTrue(action3.executed);
+ }
+
static class TestAction implements Runnable {
public boolean executed;
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index f7ad2a8..50d37ec 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -19,14 +19,18 @@
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS;
import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.policy.PhoneWindowManager.DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY;
+import static com.android.server.policy.PhoneWindowManager.TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY;
import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityTaskManager.RootTaskInfo;
import android.content.ComponentName;
import android.os.RemoteException;
import android.provider.Settings;
@@ -50,6 +54,7 @@
public void stemSingleKey_duringSetup_doNothing() {
overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(false);
@@ -65,6 +70,7 @@
public void stemSingleKey_AfterSetup_openAllApp() {
overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.overrideStartActivity();
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -83,6 +89,7 @@
STEM_PRIMARY_BUTTON_SHORT_PRESS,
SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.overrideStartActivity();
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -104,6 +111,7 @@
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true);
+
setDispatchedKeyHandler(keyEvent -> true);
sendKey(KEYCODE_STEM_PRIMARY);
@@ -131,6 +139,7 @@
STEM_PRIMARY_BUTTON_LONG_PRESS,
LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideIsUserSetupComplete(true);
@@ -144,6 +153,7 @@
STEM_PRIMARY_BUTTON_LONG_PRESS,
LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideSearchManager(null);
mPhoneWindowManager.overrideStatusBarManagerInternal();
@@ -156,7 +166,8 @@
@Test
public void stemDoubleKey_EarlyShortPress_AllAppsThenSwitchToMostRecent()
throws RemoteException {
- overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true);
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
@@ -171,14 +182,47 @@
sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
@Test
- public void stemDoubleKey_NoEarlyShortPress_SwitchToMostRecent() throws RemoteException {
+ public void stemTripleKey_EarlyShortPress_AllAppsThenBackToOriginalThenToggleA11y()
+ throws RemoteException {
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY);
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true);
+ mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true);
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+ RootTaskInfo allAppsTask = new RootTaskInfo();
+ int referenceId = 777;
+ allAppsTask.taskId = referenceId;
+ doReturn(allAppsTask)
+ .when(mPhoneWindowManager.mActivityManagerService)
+ .getFocusedRootTaskInfo();
+
+ mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ false);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertOpenAllAppView();
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
+ mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true);
+ }
+
+ @Test
+ public void stemMultiKey_NoEarlyPress_NoOpenAllApp() throws RemoteException {
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ overrideBehavior(
+ STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY);
setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+ mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true);
mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
mPhoneWindowManager.overrideIsUserSetupComplete(true);
RecentTaskInfo recentTaskInfo = new RecentTaskInfo();
@@ -189,9 +233,16 @@
sendKey(KEYCODE_STEM_PRIMARY);
sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertNotOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ mPhoneWindowManager.assertNotOpenAllAppView();
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
@Test
@@ -215,7 +266,7 @@
sendKey(KEYCODE_STEM_PRIMARY);
mPhoneWindowManager.assertNotOpenAllAppView();
- mPhoneWindowManager.assertSwitchToRecent(referenceId);
+ mPhoneWindowManager.assertSwitchToTask(referenceId);
}
private void overrideBehavior(String key, int expectedBehavior) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 0678210..7c2f7ee 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -73,6 +73,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
@@ -176,8 +177,9 @@
private Handler mHandler;
private boolean mIsTalkBackEnabled;
+ private boolean mIsTalkBackShortcutGestureEnabled;
- class TestTalkbackShortcutController extends TalkbackShortcutController {
+ private class TestTalkbackShortcutController extends TalkbackShortcutController {
TestTalkbackShortcutController(Context context) {
super(context);
}
@@ -190,13 +192,18 @@
@Override
boolean isTalkBackShortcutGestureEnabled() {
- return true;
+ return mIsTalkBackShortcutGestureEnabled;
}
}
private class TestInjector extends PhoneWindowManager.Injector {
TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
- super(context, funcs, mTestLooper.getLooper());
+ super(context, funcs);
+ }
+
+ @Override
+ Looper getLooper() {
+ return mTestLooper.getLooper();
}
AccessibilityShortcutController getAccessibilityShortcutController(
@@ -410,6 +417,10 @@
mPhoneWindowManager.mShouldEarlyShortPressOnStemPrimary = shouldEarlyShortPress;
}
+ void overrideTalkbackShortcutGestureEnabled(boolean enabled) {
+ mIsTalkBackShortcutGestureEnabled = enabled;
+ }
+
// Override assist perform function.
void overrideLongPressOnPower(int behavior) {
mPhoneWindowManager.mLongPressOnPowerBehavior = behavior;
@@ -714,7 +725,7 @@
}
void assertOpenAllAppView() {
- mTestLooper.dispatchAll();
+ moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
.startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -728,7 +739,7 @@
}
void assertActivityTargetLaunched(ComponentName targetActivity) {
- mTestLooper.dispatchAll();
+ moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
.startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
@@ -743,10 +754,15 @@
expectedModifierState, deviceBus), description(errorMsg));
}
- void assertSwitchToRecent(int persistentId) throws RemoteException {
+ void assertSwitchToTask(int persistentId) throws RemoteException {
mTestLooper.dispatchAll();
verify(mActivityManagerService,
timeout(TEST_SINGLE_KEY_DELAY_MILLIS)).startActivityFromRecents(eq(persistentId),
isNull());
}
+
+ void assertTalkBack(boolean expectEnabled) {
+ mTestLooper.dispatchAll();
+ Assert.assertEquals(expectEnabled, mIsTalkBackEnabled);
+ }
}
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 85c6f9e..f049b33 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -161,6 +161,7 @@
import com.android.internal.R;
import com.android.server.wm.ActivityRecord.State;
+import com.android.window.flags.Flags;
import org.junit.Assert;
import org.junit.Before;
@@ -202,8 +203,7 @@
}
private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
- return new TestStartingWindowOrganizer(mAtm,
- mSystemServicesTestRule.getPowerManagerWrapper());
+ return new TestStartingWindowOrganizer(mAtm);
}
@Test
@@ -321,7 +321,7 @@
}
private void ensureActivityConfiguration(ActivityRecord activity) {
- activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ activity.ensureActivityConfiguration();
}
@Test
@@ -719,7 +719,7 @@
// Clear size compat.
activity.clearSizeCompatMode();
- activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ activity.ensureActivityConfiguration();
mDisplayContent.sendNewConfiguration();
// Relaunching the app should still respect the orientation request.
@@ -820,8 +820,7 @@
task.onConfigurationChanged(newConfig);
- activity.ensureActivityConfiguration(0 /* globalChanges */,
- false /* preserveWindow */, true /* ignoreVisibility */);
+ activity.ensureActivityConfiguration(true /* ignoreVisibility */);
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token,
@@ -1564,8 +1563,7 @@
topActivity.nowVisible = true;
topActivity.setState(RESUMED, "true");
doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
- any() /* starting */, anyInt() /* configChanges */,
- anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+ any() /* starting */, anyBoolean() /* notifyClients */);
topActivity.setShowWhenLocked(true);
// Verify the stack-top activity is occluded keyguard.
@@ -1625,7 +1623,6 @@
secondActivity.finishing = true;
secondActivity.completeFinishing("test");
verify(secondActivity.mDisplayContent).ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */ , false /* preserveWindows */,
true /* notifyClients */);
// Finish the first activity
@@ -1633,7 +1630,6 @@
firstActivity.setVisibleRequested(true);
firstActivity.completeFinishing("test");
verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */,
- 0 /* configChanges */ , false /* preserveWindows */,
true /* notifyClients */);
// Remove the translucent activity and clear invocations for next test
@@ -1961,6 +1957,7 @@
display.continueUpdateOrientationForDiffOrienLaunchingApp();
assertTrue(display.isFixedRotationLaunchingApp(activity));
+ activity.stopFreezingScreen(true /* unfreezeSurfaceNow */, true /* force */);
// Simulate the rotation has been updated to previous one, e.g. sensor updates before the
// remote rotation is completed.
doReturn(originalRotation).when(displayRotation).rotationForOrientation(
@@ -1971,14 +1968,12 @@
activity.finishFixedRotationTransform();
final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
assertNotNull(rotationAnim);
- rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
// Because the display doesn't rotate, the rotated activity needs to cancel the fixed
// rotation. There should be a rotation animation to cover the change of activity.
verify(activity).onCancelFixedRotationTransform(rotatedInfo.rotation);
assertTrue(activity.isFreezingScreen());
assertFalse(displayRotation.isRotatingSeamlessly());
- assertTrue(rotationAnim.isRotating());
// Simulate the remote rotation has completed and the configuration doesn't change, then
// the rotated activity should also be restored by clearing the transform.
@@ -2645,6 +2640,9 @@
// Can specify orientation if the current orientation candidate is orientation behind.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
activity.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ activity.makeFinishingLocked();
+ assertEquals("Finishing activity must not report orientation",
+ SCREEN_ORIENTATION_UNSET, activity.getOrientation(SCREEN_ORIENTATION_BEHIND));
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
.setActivityTheme(android.R.style.Theme_Translucent)
@@ -3371,7 +3369,7 @@
// to client if the app didn't request IME visible.
assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
- if (mWm.mFlags.mWindowStateResizeItemFlag) {
+ if (Flags.bundleClientTransactionFlag()) {
verify(app2.getProcess()).scheduleClientTransactionItem(
isA(WindowStateResizeItem.class));
} else {
@@ -3698,7 +3696,7 @@
doReturn(false).when(activity).showToCurrentUser();
spyOn(taskFragment);
doReturn(false).when(taskFragment).shouldBeVisible(any());
- display.ensureActivitiesVisible(null, 0, false, false);
+ display.ensureActivitiesVisible(null /* starting */, false /* notifyClients */);
assertFalse(activity.isVisibleRequested());
}
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 a11079b..0c1fbf3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -1221,7 +1221,7 @@
public void testTransitionGoodToGoForTaskFragments_detachedApp() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+ registerTaskFragmentOrganizer(iOrganizer);
final Task task = createTask(mDisplayContent);
final TaskFragment changeTaskFragment =
createTaskFragmentWithEmbeddedActivity(task, organizer);
@@ -1265,7 +1265,7 @@
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter);
definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter);
definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, adapter);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+ registerTaskFragmentOrganizer(iOrganizer);
mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index ba8c94d..9950541 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -439,7 +439,7 @@
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final ITaskFragmentOrganizer iOrganizer =
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+ registerTaskFragmentOrganizer(iOrganizer);
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
.setOrganizer(organizer)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index c757457..09f677e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -172,7 +172,7 @@
@Test
public void testScheduleTransactionItemUnlocked() throws RemoteException {
// Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItemUnlocked(mNonBinderClient, mTransactionItem);
+ mLifecycleManager.scheduleTransactionItemNow(mNonBinderClient, mTransactionItem);
// Dispatch immediately.
assertTrue(mLifecycleManager.mPendingTransactions.isEmpty());
@@ -217,6 +217,8 @@
@Test
public void testDispatchPendingTransactions() throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
mLifecycleManager.mPendingTransactions.put(mClientBinder, mTransaction);
mLifecycleManager.dispatchPendingTransactions();
diff --git a/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
index 1f7b65e..c187263 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
@@ -20,10 +20,13 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import android.app.GameManagerInternal;
+import android.content.pm.ApplicationInfo;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -90,6 +93,14 @@
public void testGetCompatScale_noGameManager() {
assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
0.01f);
- }
+ final ApplicationInfo info = new ApplicationInfo();
+ // Any non-zero value without FLAG_SUPPORTS_*_SCREENS.
+ info.flags = ApplicationInfo.FLAG_HAS_CODE;
+ info.packageName = info.sourceDir = "legacy.app";
+ mAtm.mCompatModePackages.compatibilityInfoForPackageLocked(info);
+ assertTrue(mAtm.mCompatModePackages.useLegacyScreenCompatMode(info.packageName));
+ mAtm.mCompatModePackages.handlePackageUninstalledLocked(info.packageName);
+ assertFalse(mAtm.mCompatModePackages.useLegacyScreenCompatMode(info.packageName));
+ }
}
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 dfe79bf..6497ee9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -75,7 +75,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
@@ -490,7 +489,7 @@
newOverrideConfig.fontScale += 0.3;
defaultDisplay.updateDisplayOverrideConfigurationLocked(newOverrideConfig,
- null /* starting */, false /* deferResume */, null /* result */);
+ null /* starting */, false /* deferResume */);
// Check that global configuration is updated, as we've updated default display's config.
Configuration globalConfig = mWm.mRoot.getConfiguration();
@@ -499,7 +498,7 @@
// Return back to original values.
defaultDisplay.updateDisplayOverrideConfigurationLocked(currentConfig,
- null /* starting */, false /* deferResume */, null /* result */);
+ null /* starting */, false /* deferResume */);
globalConfig = mWm.mRoot.getConfiguration();
assertEquals(currentConfig.densityDpi, globalConfig.densityDpi);
assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
@@ -1176,7 +1175,7 @@
activity.setRequestedOrientation(newOrientation);
verify(dc, never()).updateDisplayOverrideConfigurationLocked(any(), eq(activity),
- anyBoolean(), same(null));
+ anyBoolean());
assertEquals(ROTATION_180, dc.getRotation());
}
@@ -2123,10 +2122,8 @@
// Once transition starts, rotation is applied and transition shows DC rotating.
testPlayer.startTransition();
waitUntilHandlersIdle();
- verify(activity1).ensureActivityConfiguration(anyInt(), anyBoolean(), anyBoolean(),
- anyBoolean());
- verify(activity2).ensureActivityConfiguration(anyInt(), anyBoolean(), anyBoolean(),
- anyBoolean());
+ verify(activity1).ensureActivityConfiguration(anyBoolean(), anyBoolean());
+ verify(activity2).ensureActivityConfiguration(anyBoolean(), anyBoolean());
assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
assertNotNull(testPlayer.mLastReady);
assertTrue(testPlayer.mController.isPlaying());
@@ -2248,11 +2245,11 @@
// The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice.
assertFalse(called[0]);
called[0] = true;
- mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
+ mDisplayContent.ensureActivitiesVisible(null, false);
return null;
- }).when(mockTda).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean());
+ }).when(mockTda).ensureActivitiesVisible(any(), anyBoolean());
- mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
+ mDisplayContent.ensureActivitiesVisible(null, false);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 985be42..4e4bbfe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -697,6 +697,31 @@
@Test
@EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutSystem_returnsUser()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
+ prepareActivityThatShouldApplyUserFullscreenOverride();
+
+ // fullscreen override still applied
+ assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutUser_returnsUser()
+ throws Exception {
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
+ /* value */ false);
+ prepareActivityThatShouldApplyUserFullscreenOverride();
+
+ // fullscreen override still applied
+ assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUnchanged()
throws Exception {
mDisplayContent.setIgnoreOrientationRequest(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 8de45b0..32b3558 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -106,8 +106,7 @@
topActivity.getRootTask().moveToFront("testRecentsActivityVisiblility");
doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
- any() /* starting */, anyInt() /* configChanges */,
- anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+ any() /* starting */, anyBoolean() /* notifyClients */);
RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
mRecentsComponent, true /* getRecentsAnimation */);
@@ -178,8 +177,7 @@
mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
null /* recentsAnimationRunner */);
- verify(recentsActivity).ensureActivityConfiguration(anyInt() /* globalChanges */,
- anyBoolean() /* preserveWindow */, eq(true) /* ignoreVisibility */);
+ verify(recentsActivity).ensureActivityConfiguration(eq(true) /* ignoreVisibility */);
assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity);
}
@@ -199,8 +197,7 @@
"testRestartRecentsActivity");
doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
- any() /* starting */, anyInt() /* configChanges */,
- anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+ any() /* starting */, anyBoolean() /* notifyClients */);
doReturn(app).when(mAtm).getProcessController(eq(recentActivity.processName), anyInt());
doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
@@ -354,8 +351,7 @@
doReturn(TEST_USER_ID).when(mAtm).getCurrentUserId();
doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
- any() /* starting */, anyInt() /* configChanges */,
- anyBoolean() /* preserveWindows */, anyBoolean() /* notifyClients */);
+ any() /* starting */, anyBoolean() /* notifyClients */);
startRecentsActivity(otherUserHomeActivity.getTask().getBaseIntent().getComponent(),
true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 89cd726..527ea0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -140,7 +140,7 @@
final WindowContainer parent = activity1.getTask().getParent();
assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
- mDisplayContent.mClosingApps.add(activity2);
+ activity2.setVisibleRequested(false);
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation());
}
@@ -823,8 +823,7 @@
.build();
doReturn(false).when(secondActivity).occludesParent();
- homeRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
+ homeRootTask.ensureActivitiesVisible(null /* starting */);
assertTrue(firstActivity.shouldBeVisible());
}
@@ -1419,8 +1418,7 @@
// Any common path that updates activity visibility should clear the unknown visibility
// records that are no longer visible according to hierarchy.
- task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
+ task.ensureActivitiesVisible(null /* starting */);
// Assume the top activity relayouted, just remove it directly.
unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
// All unresolved records should be removed.
@@ -1441,8 +1439,7 @@
doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
anyBoolean());
- task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
+ task.ensureActivitiesVisible(null /* starting */);
verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */,
anyBoolean());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index c3102e0..5518c60 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -947,7 +947,7 @@
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
- mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ mActivity.ensureActivityConfiguration();
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
assertEquals(newDisplayBounds.height(), currentBounds.height());
@@ -4858,7 +4858,7 @@
}
// Make sure to use the provided configuration to construct the size compat fields.
activity.clearSizeCompatMode();
- activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ activity.ensureActivityConfiguration();
// Make sure the display configuration reflects the change of activity.
if (activity.mDisplayContent.updateOrientation()) {
activity.mDisplayContent.sendNewConfiguration();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index ef427bb..a8b2178 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.server.wm.CtsWindowInfoUtils.dumpWindowsOnScreen;
+import static android.server.wm.CtsWindowInfoUtils.assertAndDumpWindowState;
import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus;
import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -129,37 +129,22 @@
mScvh2.setView(mView2, lp2);
});
- boolean wasVisible = waitForWindowVisible(mView1);
- if (!wasVisible) {
- dumpWindowsOnScreen(TAG, "requestFocusWithMultipleWindows");
- }
- assertTrue("Failed to wait for view1", wasVisible);
-
- wasVisible = waitForWindowVisible(mView2);
- if (!wasVisible) {
- dumpWindowsOnScreen(TAG, "requestFocusWithMultipleWindows-not visible");
- }
- assertTrue("Failed to wait for view2", wasVisible);
+ assertAndDumpWindowState(TAG, "Failed to wait for view1", waitForWindowVisible(mView1));
+ assertAndDumpWindowState(TAG, "Failed to wait for view2", waitForWindowVisible(mView2));
IWindow window = IWindow.Stub.asInterface(mSurfaceView.getWindowToken());
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
mScvh1.getInputTransferToken(), true);
- boolean gainedFocus = waitForWindowFocus(mView1, true);
- if (!gainedFocus) {
- dumpWindowsOnScreen(TAG, "requestFocusWithMultipleWindows-view1 not focus");
- }
- assertTrue("Failed to gain focus for view1", gainedFocus);
+ assertAndDumpWindowState(TAG, "Failed to wait for view1 focus",
+ waitForWindowFocus(mView1, true));
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
mScvh2.getInputTransferToken(), true);
- gainedFocus = waitForWindowFocus(mView2, true);
- if (!gainedFocus) {
- dumpWindowsOnScreen(TAG, "requestFocusWithMultipleWindows-view2 not focus");
- }
- assertTrue("Failed to gain focus for view2", gainedFocus);
+ assertAndDumpWindowState(TAG, "Failed to wait for view2 focus",
+ waitForWindowFocus(mView2, true));
}
private static class TestWindowlessWindowManager extends WindowlessWindowManager {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index e65a9fe..b45fa31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import android.os.Handler;
@@ -35,7 +37,7 @@
new DexmakerShareClassLoaderRule();
@Rule(order = 1)
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@Rule(order = 2)
public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(
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 51f0404..90493d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -134,7 +134,6 @@
private StaticMockitoSession mMockitoSession;
private ActivityTaskManagerService mAtmService;
private WindowManagerService mWmService;
- private WindowState.PowerManagerWrapper mPowerManagerWrapper;
private InputManagerService mImService;
private InputChannel mInputChannel;
private Runnable mOnBeforeServicesCreated;
@@ -360,7 +359,6 @@
}
private void setUpWindowManagerService() {
- mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
TestWindowManagerPolicy wmPolicy = new TestWindowManagerPolicy();
TestDisplayWindowSettingsProvider testDisplayWindowSettingsProvider =
new TestDisplayWindowSettingsProvider();
@@ -377,8 +375,7 @@
// Always keep things awake.
doReturn(true).when(mWmService.mRoot).hasAwakeDisplay();
// Called when moving activity to pinned stack.
- doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(),
- anyInt(), anyBoolean(), anyBoolean());
+ doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(), anyBoolean());
spyOn(mWmService.mDisplayWindowSettings);
spyOn(mWmService.mDisplayWindowSettingsProvider);
@@ -485,10 +482,6 @@
return mAtmService;
}
- WindowState.PowerManagerWrapper getPowerManagerWrapper() {
- return mPowerManagerWrapper;
- }
-
/** Creates a no-op wakelock object. */
PowerManager.WakeLock createStubbedWakeLock(boolean needVerification) {
if (needVerification) {
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 06f29c2..d36ee2c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -74,6 +74,7 @@
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -104,6 +105,8 @@
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -123,7 +126,6 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
- private static final int TASK_ID = 10;
private TaskFragmentOrganizerController mController;
private WindowOrganizerController mWindowOrganizerController;
@@ -143,6 +145,8 @@
private TaskFragmentInfo mTaskFragmentInfo;
@Mock
private Task mTask;
+ @Mock
+ private IApplicationThread mAppThread;
@Captor
private ArgumentCaptor<TaskFragmentTransaction> mTransactionCaptor;
@@ -178,12 +182,21 @@
doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+ doReturn(mAppThread).when(mController).getAppThread(anyInt(), anyInt());
+ doAnswer(invocation -> {
+ final ITaskFragmentOrganizer organizer =
+ (ITaskFragmentOrganizer) invocation.getArguments()[0];
+ final TaskFragmentTransaction taskFragmentTransaction =
+ (TaskFragmentTransaction) invocation.getArguments()[1];
+ organizer.onTransactionReady(taskFragmentTransaction);
+ return null;
+ }).when(mAppThread).scheduleTaskFragmentTransaction(any(), any());
// To prevent it from calling the real server.
doNothing().when(mOrganizer).applyTransaction(any(), anyInt(), anyBoolean());
doNothing().when(mOrganizer).onTransactionHandled(any(), any(), anyInt(), anyBoolean());
- mController.registerOrganizer(mIOrganizer);
+ registerTaskFragmentOrganizer(mIOrganizer);
}
@Test
@@ -204,12 +217,15 @@
}
@Test
- public void testOnTaskFragmentAppeared() {
+ public void testOnTaskFragmentAppeared_throughTaskFragmentOrganizer() throws RemoteException {
+ mSetFlagsRule.disableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
// No-op when the TaskFragment is not attached.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer, never()).onTransactionReady(any());
+ verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
// Send callback when the TaskFragment is attached.
setupMockParent(mTaskFragment, mTask);
@@ -219,12 +235,40 @@
assertTaskFragmentParentInfoChangedTransaction(mTask);
assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
+ verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
+ }
+
+ @Test
+ public void testOnTaskFragmentAppeared_throughApplicationThread() throws RemoteException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+ // Re-register the organizer in case the flag was disabled during setup.
+ mController.unregisterOrganizer(mIOrganizer);
+ registerTaskFragmentOrganizer(mIOrganizer);
+
+ // No-op when the TaskFragment is not attached.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTransactionReady(any());
+ verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
+
+ // Send callback when the TaskFragment is attached.
+ setupMockParent(mTaskFragment, mTask);
+
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mAppThread).scheduleTaskFragmentTransaction(eq(mIOrganizer), any());
+ assertTaskFragmentParentInfoChangedTransaction(mTask);
+ assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
}
@Test
public void testOnTaskFragmentAppeared_systemOrganizer() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
mController.unregisterOrganizer(mIOrganizer);
- mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
// No-op when the TaskFragment is not attached.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
@@ -565,8 +609,10 @@
@Test
public void testApplyTransaction_allowRemoteTransitionForSystemOrganizer() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
mController.unregisterOrganizer(mIOrganizer);
- mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
@@ -1056,7 +1102,7 @@
// Nothing should happen as the organizer is not registered.
assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
- mController.registerOrganizer(mIOrganizer);
+ registerTaskFragmentOrganizer(mIOrganizer);
assertApplyTransactionAllowed(mTransaction);
// Successfully created when the organizer is registered.
@@ -1692,8 +1738,10 @@
@Test
public void testApplyTransaction_reorderToBottomOfTask() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
mController.unregisterOrganizer(mIOrganizer);
- mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
final Task task = createTask(mDisplayContent);
// Create a non-embedded Activity at the bottom.
final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
@@ -1727,8 +1775,10 @@
@Test
public void testApplyTransaction_reorderToTopOfTask() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
mController.unregisterOrganizer(mIOrganizer);
- mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
final Task task = createTask(mDisplayContent);
// Create a non-embedded Activity at the bottom.
final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
@@ -1762,9 +1812,11 @@
@Test
public void testApplyTransaction_createTaskFragmentDecorSurface() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
// TODO(b/293654166) remove system organizer requirement once security review is cleared.
mController.unregisterOrganizer(mIOrganizer);
- mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
final Task task = createTask(mDisplayContent);
final TaskFragment tf = createTaskFragment(task);
@@ -1779,9 +1831,11 @@
@Test
public void testApplyTransaction_removeTaskFragmentDecorSurface() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
// TODO(b/293654166) remove system organizer requirement once security review is cleared.
mController.unregisterOrganizer(mIOrganizer);
- mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ registerTaskFragmentOrganizer(mIOrganizer, true /* isSystemOrganizer */);
final Task task = createTask(mDisplayContent);
final TaskFragment tf = createTaskFragment(task);
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 ec068be..875e708 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -90,8 +90,7 @@
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken()
.asBinder());
- mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController
- .registerOrganizer(mIOrganizer);
+ registerTaskFragmentOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
.setOrganizer(mOrganizer)
@@ -269,8 +268,7 @@
mTaskFragment.getTask().addChild(activityBelow, 0);
// Ensure the activity below is visible
- mTaskFragment.getTask().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
+ mTaskFragment.getTask().ensureActivitiesVisible(null /* starting */);
assertEquals(true, activityBelow.isVisibleRequested());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index da7612b..45e1e95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -339,16 +339,14 @@
// Check visibility of occluded tasks
doReturn(false).when(leafTask1).shouldBeVisible(any());
doReturn(true).when(leafTask2).shouldBeVisible(any());
- rootTask.ensureActivitiesVisible(
- null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
+ rootTask.ensureActivitiesVisible(null /* starting */);
assertFalse(activity1.isVisible());
assertTrue(activity2.isVisible());
// Check visibility of not occluded tasks
doReturn(true).when(leafTask1).shouldBeVisible(any());
doReturn(true).when(leafTask2).shouldBeVisible(any());
- rootTask.ensureActivitiesVisible(
- null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
+ rootTask.ensureActivitiesVisible(null /* starting */);
assertTrue(activity1.isVisible());
assertTrue(activity2.isVisible());
}
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 bd111ad..52e2d8a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -89,8 +89,8 @@
}
@Override
- public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
- int policyFlags) {
+ public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
+ long whenNanos, int policyFlags) {
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 71447e7..0514943 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1495,8 +1495,7 @@
verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1));
enteringAnimReports.clear();
- doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(),
- anyInt(), anyBoolean(), anyBoolean());
+ doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), anyBoolean());
final boolean[] wasInFinishingTransition = { false };
controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
@Override
@@ -1640,7 +1639,7 @@
final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
assertFalse(nonEmbeddedActivity.isEmbedded());
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ registerTaskFragmentOrganizer(
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
@@ -1690,7 +1689,7 @@
task.getConfiguration().windowConfiguration.setBounds(taskBounds);
final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ registerTaskFragmentOrganizer(
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
@@ -1819,7 +1818,7 @@
// Skip manipulate the SurfaceControl.
doNothing().when(activity).setDropInputMode(anyInt());
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ registerTaskFragmentOrganizer(
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
@@ -1850,7 +1849,7 @@
// Test background color for Activity and embedded TaskFragment.
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ registerTaskFragmentOrganizer(
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
final Task task = createTask(mDisplayContent);
final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
index ac49839..6a15b05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.server.wm.CtsWindowInfoUtils.assertAndDumpWindowState;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
@@ -138,11 +139,8 @@
return false;
}, TIMEOUT_S, TimeUnit.SECONDS);
- if (!foundTrusted[0]) {
- CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
- }
-
- assertTrue("Failed to find window or was not marked trusted", foundTrusted[0]);
+ assertAndDumpWindowState(TAG, "Failed to find window or was not marked trusted",
+ foundTrusted[0]);
}
private void testTrustedOverlayChildHelper(boolean expectedTrustedChild)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 28e0c6b..74aabe1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -40,11 +40,11 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.server.wm.testing.Assert.assertThrows;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
+import static com.android.server.wm.testing.Assert.assertThrows;
import static com.google.common.truth.Truth.assertThat;
@@ -92,6 +92,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.wm.TaskOrganizerController.PendingTaskEvent;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -585,6 +586,8 @@
@Test
public void testTaskFragmentHiddenFocusableTranslucentChanges() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
removeGlobalMinSizeRestriction();
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
@@ -660,6 +663,8 @@
private void testTaskFragmentChangesWithoutSystemOrganizerThrowException(
BiConsumer<WindowContainerTransaction, WindowContainerToken> addOp) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
+
removeGlobalMinSizeRestriction();
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
@@ -1735,11 +1740,9 @@
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final ITaskFragmentOrganizer organizerInterface =
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
- mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController
- .registerOrganizerInternal(
- ITaskFragmentOrganizer.Stub.asInterface(
- organizer.getOrganizerToken().asBinder()),
- isSystemOrganizer);
+ registerTaskFragmentOrganizer(
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()),
+ isSystemOrganizer);
t.setTaskFragmentOrganizer(organizerInterface);
return organizer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index e31ee11..400e4b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -38,7 +38,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -306,10 +305,12 @@
@Test
public void testCachedStateConfigurationChange() throws RemoteException {
- doNothing().when(mClientLifecycleManager).scheduleTransactionItemUnlocked(any(), any());
+ doNothing().when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
final IApplicationThread thread = mWpc.getThread();
final Configuration newConfig = new Configuration(mWpc.getConfiguration());
newConfig.densityDpi += 100;
+ mWpc.mWindowSession = getTestSession();
+ mWpc.mWindowSession.onWindowAdded(mock(WindowState.class));
// Non-cached state will send the change directly.
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
clearInvocations(mClientLifecycleManager);
@@ -322,13 +323,13 @@
newConfig.densityDpi += 100;
mWpc.onConfigurationChanged(newConfig);
verify(mClientLifecycleManager, never()).scheduleTransactionItem(eq(thread), any());
- verify(mClientLifecycleManager, never()).scheduleTransactionItemUnlocked(eq(thread), any());
+ verify(mClientLifecycleManager, never()).scheduleTransactionItemNow(eq(thread), any());
// Cached -> non-cached will send the previous deferred config immediately.
mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
final ArgumentCaptor<ConfigurationChangeItem> captor =
ArgumentCaptor.forClass(ConfigurationChangeItem.class);
- verify(mClientLifecycleManager).scheduleTransactionItemUnlocked(
+ verify(mClientLifecycleManager).scheduleTransactionItemNow(
eq(thread), captor.capture());
final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
captor.getValue().preExecute(client);
@@ -432,7 +433,7 @@
mWpc.updateAppSpecificSettingsForAllActivitiesInPackage(DEFAULT_COMPONENT_PACKAGE_NAME,
Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"),
GRAMMATICAL_GENDER_NOT_SPECIFIED);
- verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean());
+ verify(activity).ensureActivityConfiguration();
}
@Test
@@ -443,7 +444,7 @@
Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"),
GRAMMATICAL_GENDER_NOT_SPECIFIED);
verify(activity, never()).applyAppSpecificConfig(anyInt(), any(), anyInt());
- verify(activity, never()).ensureActivityConfiguration(anyInt(), anyBoolean());
+ verify(activity, never()).ensureActivityConfiguration();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 2007f68..f24baba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -52,7 +52,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -368,28 +367,26 @@
firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- final WindowState.PowerManagerWrapper powerManagerWrapper =
- mSystemServicesTestRule.getPowerManagerWrapper();
- reset(powerManagerWrapper);
+ final var powerManager = mWm.mPowerManager;
+ clearInvocations(powerManager);
firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
- reset(powerManagerWrapper);
+ clearInvocations(powerManager);
secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
}
private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow,
boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) {
- final WindowState.PowerManagerWrapper powerManagerWrapper =
- mSystemServicesTestRule.getPowerManagerWrapper();
- reset(powerManagerWrapper);
+ final var powerManager = mWm.mPowerManager;
+ clearInvocations(powerManager);
appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */);
if (expectedWakeupCalled) {
- verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager).wakeUp(anyLong(), anyInt(), anyString());
} else {
- verify(powerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
}
// If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
// because the state will be consumed.
@@ -817,7 +814,7 @@
@Test
public void testEmbeddedActivityResizing_clearAllDrawn() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ registerTaskFragmentOrganizer(
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
final Task task = createTask(mDisplayContent);
final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 616a23e..60e84b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -107,6 +107,7 @@
import android.view.WindowManager.DisplayImePolicy;
import android.view.inputmethod.ImeTracker;
import android.window.ClientWindowFrames;
+import android.window.ITaskFragmentOrganizer;
import android.window.ITransitionPlayer;
import android.window.ScreenCapture;
import android.window.StartingWindowInfo;
@@ -637,14 +638,12 @@
WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) {
return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId),
- ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow,
- mSystemServicesTestRule.getPowerManagerWrapper());
+ ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow);
}
static WindowState createWindow(WindowState parent, int type, WindowToken token,
String name, int ownerId, int userId, boolean ownerCanAddInternalSystemWindow,
- WindowManagerService service, Session session, IWindow iWindow,
- WindowState.PowerManagerWrapper powerManagerWrapper) {
+ WindowManagerService service, Session session, IWindow iWindow) {
SystemServicesTestRule.checkHoldsLock(service.mGlobalLock);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
@@ -652,9 +651,7 @@
attrs.packageName = "test";
final WindowState w = new WindowState(service, session, iWindow, token, parent,
- OP_NONE, attrs, VISIBLE, ownerId, userId,
- ownerCanAddInternalSystemWindow,
- powerManagerWrapper);
+ OP_NONE, attrs, VISIBLE, ownerId, userId, ownerCanAddInternalSystemWindow);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
token.addWindow(w);
@@ -895,6 +892,22 @@
return taskFragment;
}
+ /** @see TaskFragmentOrganizerController#registerOrganizer */
+ void registerTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+ registerTaskFragmentOrganizer(organizer, false /* isSystemOrganizer */);
+ }
+
+ /** @see TaskFragmentOrganizerController#registerOrganizer */
+ void registerTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer,
+ boolean isSystemOrganizer) {
+ // Ensure there is an IApplicationThread to dispatch TaskFragmentTransaction.
+ if (mAtm.mProcessMap.getProcess(WindowManagerService.MY_PID) == null) {
+ mSystemServicesTestRule.addProcess("pkgName", "procName",
+ WindowManagerService.MY_PID, WindowManagerService.MY_UID);
+ }
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer);
+ }
+
/** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
DisplayContent createNewDisplay() {
return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL);
@@ -1738,17 +1751,14 @@
static class TestStartingWindowOrganizer extends WindowOrganizerTests.StubOrganizer {
private final ActivityTaskManagerService mAtm;
private final WindowManagerService mWMService;
- private final WindowState.PowerManagerWrapper mPowerManagerWrapper;
private Runnable mRunnableWhenAddingSplashScreen;
private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>();
private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>();
- TestStartingWindowOrganizer(ActivityTaskManagerService service,
- WindowState.PowerManagerWrapper powerManagerWrapper) {
+ TestStartingWindowOrganizer(ActivityTaskManagerService service) {
mAtm = service;
mWMService = mAtm.mWindowManager;
- mPowerManagerWrapper = powerManagerWrapper;
mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
}
@@ -1767,8 +1777,7 @@
final WindowState window = WindowTestsBase.createWindow(null,
TYPE_APPLICATION_STARTING, activity,
"Starting window", 0 /* ownerId */, 0 /* userId*/,
- false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow,
- mPowerManagerWrapper);
+ false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow);
activity.mStartingWindow = window;
mAppWindowMap.put(info.appToken, window);
mTaskAppMap.put(info.taskInfo.taskId, info.appToken);
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
index bfc1771..336bfdd 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
@@ -35,6 +35,7 @@
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.Keep;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.RingBuffer;
import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType;
@@ -178,6 +179,7 @@
}
}
+ @Keep
public static final class BroadcastEvent implements Data {
public int sourceUid;
public int targetUserId;
@@ -198,6 +200,7 @@
}
}
+ @Keep
public static final class NotificationEvent implements Data {
public int type;
public String packageName;
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 4c978ad..2445f51 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -28,6 +28,7 @@
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.usage.ExternalStorageStats;
+import android.app.usage.Flags;
import android.app.usage.IStorageStatsManager;
import android.app.usage.StorageStats;
import android.app.usage.UsageStatsManagerInternal;
@@ -434,6 +435,7 @@
final long[] ceDataInodes = new long[packageNames.length];
String[] codePaths = new String[0];
+ final PackageStats stats = new PackageStats(TAG);
for (int i = 0; i < packageNames.length; i++) {
try {
final ApplicationInfo appInfo = mPackage.getApplicationInfoAsUser(packageNames[i],
@@ -443,7 +445,11 @@
} else {
if (appInfo.getCodePath() != null) {
codePaths = ArrayUtils.appendElement(String.class, codePaths,
- appInfo.getCodePath());
+ appInfo.getCodePath());
+ }
+ if (Flags.getAppBytesByDataTypeApi()) {
+ computeAppStatsByDataTypes(
+ stats, appInfo.sourceDir);
}
}
} catch (NameNotFoundException e) {
@@ -451,7 +457,6 @@
}
}
- final PackageStats stats = new PackageStats(TAG);
try {
mInstaller.getAppSize(volumeUuid, packageNames, userId, getDefaultFlags(),
appId, ceDataInodes, codePaths, stats);
@@ -587,6 +592,9 @@
res.codeBytes = stats.codeSize + stats.externalCodeSize;
res.dataBytes = stats.dataSize + stats.externalDataSize;
res.cacheBytes = stats.cacheSize + stats.externalCacheSize;
+ res.apkBytes = stats.apkSize;
+ res.libBytes = stats.libSize;
+ res.dmBytes = stats.dmSize;
res.externalCacheBytes = stats.externalCacheSize;
return res;
}
@@ -894,4 +902,61 @@
mStorageStatsAugmenters.add(Pair.create(tag, storageStatsAugmenter));
}
}
+
+ private long getDirBytes(File dir) {
+ if (!dir.isDirectory()) {
+ return 0;
+ }
+
+ long size = 0;
+ try {
+ for (File file : dir.listFiles()) {
+ if (file.isFile()) {
+ size += file.length();
+ continue;
+ }
+ if (file.isDirectory()) {
+ size += getDirBytes(file);
+ }
+ }
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed to list directory " + dir.getName());
+ }
+
+ return size;
+ }
+
+ private long getFileBytesInDir(File dir, String suffix) {
+ if (!dir.isDirectory()) {
+ return 0;
+ }
+
+ long size = 0;
+ try {
+ for (File file : dir.listFiles()) {
+ if (file.isFile() && file.getName().endsWith(suffix)) {
+ size += file.length();
+ }
+ }
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed to list directory " + dir.getName());
+ }
+
+ return size;
+ }
+
+ private void computeAppStatsByDataTypes(
+ PackageStats stats, String sourceDirName) {
+
+ // Get apk, lib, dm file sizes.
+ File srcDir = new File(sourceDirName);
+ if (srcDir.isFile()) {
+ sourceDirName = srcDir.getParent();
+ srcDir = new File(sourceDirName);
+ }
+
+ stats.apkSize += getFileBytesInDir(srcDir, ".apk");
+ stats.dmSize += getFileBytesInDir(srcDir, ".dm");
+ stats.libSize += getDirBytes(new File(sourceDirName + "/lib/"));
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index ccd4ce0..08f719e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1121,13 +1121,8 @@
switch (event.mEventType) {
case Event.ACTIVITY_RESUMED:
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
- uid,
- event.mPackage,
- "",
- FrameworkStatsLog
- .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND);
+ logAppUsageEventReportedAtomLocked(Event.ACTIVITY_RESUMED, uid, event.mPackage);
+
// check if this activity has already been resumed
if (mVisibleActivities.get(event.mInstanceId) != null) break;
final String usageSourcePackage = getUsageSourcePackage(event);
@@ -1172,13 +1167,8 @@
usageSourcePackage2);
mVisibleActivities.put(event.mInstanceId, pausedData);
} else {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
- uid,
- event.mPackage,
- "",
- FrameworkStatsLog
- .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
+ logAppUsageEventReportedAtomLocked(Event.ACTIVITY_PAUSED, uid,
+ event.mPackage);
}
pausedData.lastEvent = Event.ACTIVITY_PAUSED;
@@ -1203,13 +1193,8 @@
}
if (prevData.lastEvent != Event.ACTIVITY_PAUSED) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
- uid,
- event.mPackage,
- "",
- FrameworkStatsLog
- .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
+ logAppUsageEventReportedAtomLocked(Event.ACTIVITY_PAUSED, uid,
+ event.mPackage);
}
ArraySet<String> tokens;
@@ -1244,11 +1229,19 @@
}
break;
case Event.USER_INTERACTION:
- // Fall through
+ logAppUsageEventReportedAtomLocked(Event.USER_INTERACTION, uid, event.mPackage);
+ // Fall through.
case Event.APP_COMPONENT_USED:
convertToSystemTimeLocked(event);
mLastTimeComponentUsedGlobal.put(event.mPackage, event.mTimeStamp);
break;
+ case Event.SHORTCUT_INVOCATION:
+ case Event.CHOOSER_ACTION:
+ case Event.STANDBY_BUCKET_CHANGED:
+ case Event.FOREGROUND_SERVICE_START:
+ case Event.FOREGROUND_SERVICE_STOP:
+ logAppUsageEventReportedAtomLocked(event.mEventType, uid, event.mPackage);
+ break;
}
final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
@@ -1261,6 +1254,45 @@
mIoHandler.obtainMessage(MSG_NOTIFY_USAGE_EVENT_LISTENER, userId, 0, event).sendToTarget();
}
+ @GuardedBy("mLock")
+ private void logAppUsageEventReportedAtomLocked(int eventType, int uid, String packageName) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED, uid, packageName,
+ "", getAppUsageEventOccurredAtomEventType(eventType));
+ }
+
+ /** Make sure align with the EventType defined in the AppUsageEventOccurred atom. */
+ private int getAppUsageEventOccurredAtomEventType(int eventType) {
+ switch (eventType) {
+ case Event.ACTIVITY_RESUMED:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND;
+ case Event.ACTIVITY_PAUSED:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND;
+ case Event.USER_INTERACTION:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__USER_INTERACTION;
+ case Event.SHORTCUT_INVOCATION:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__SHORTCUT_INVOCATION;
+ case Event.CHOOSER_ACTION:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__CHOOSER_ACTION;
+ case Event.STANDBY_BUCKET_CHANGED:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__STANDBY_BUCKET_CHANGED;
+ case Event.FOREGROUND_SERVICE_START:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__FOREGROUND_SERVICE_START;
+ case Event.FOREGROUND_SERVICE_STOP:
+ return FrameworkStatsLog
+ .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__FOREGROUND_SERVICE_STOP;
+ default:
+ Slog.w(TAG, "Unsupported usage event logging: " + eventType);
+ return -1;
+ }
+ }
+
private String getUsageSourcePackage(Event event) {
switch(mUsageSource) {
case USAGE_SOURCE_CURRENT_ACTIVITY:
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 1dc5dcf..3a0a6ab 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -35,4 +35,7 @@
"android.hardware.usb-V1.3-java",
"android.hardware.usb-V3-java",
],
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
index 4720d27..f9fa9b7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
@@ -106,96 +106,104 @@
@Override
public void onAttentionGained() {
Slog.v(TAG, "BinderCallback#onAttentionGained");
- mEgressingData = true;
- if (mAttentionListener == null) {
- return;
- }
- try {
- mAttentionListener.onAttentionGained();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering attention gained event.", e);
- try {
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_ATTENTION_STATE,
- "Attention listener failed to switch to GAINED state."));
- } catch (RemoteException ex) {
- Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ synchronized (mLock) {
+ mEgressingData = true;
+ if (mAttentionListener == null) {
+ return;
}
- return;
+ try {
+ mAttentionListener.onAttentionGained();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering attention gained event.", e);
+ try {
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_ATTENTION_STATE,
+ "Attention listener fails to switch to GAINED state."));
+ } catch (RemoteException ex) {
+ Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ }
+ }
}
}
@Override
public void onAttentionLost() {
Slog.v(TAG, "BinderCallback#onAttentionLost");
- mEgressingData = false;
- if (mAttentionListener == null) {
- return;
- }
- try {
- mAttentionListener.onAttentionLost();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering attention lost event.", e);
- try {
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_ATTENTION_STATE,
- "Attention listener failed to switch to LOST state."));
- } catch (RemoteException ex) {
- Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ synchronized (mLock) {
+ mEgressingData = false;
+ if (mAttentionListener == null) {
+ return;
}
- return;
+ try {
+ mAttentionListener.onAttentionLost();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering attention lost event.", e);
+ try {
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_ATTENTION_STATE,
+ "Attention listener fails to switch to LOST state."));
+ } catch (RemoteException ex) {
+ Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
+ }
+ }
}
}
@Override
public void onQueryDetected(@NonNull String partialQuery) throws RemoteException {
- Objects.requireNonNull(partialQuery);
Slog.v(TAG, "BinderCallback#onQueryDetected");
- if (!mEgressingData) {
- Slog.v(TAG, "Query should not be egressed within the unattention state.");
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_STREAMING_STATE,
- "Cannot stream queries without attention signals."));
- return;
+ synchronized (mLock) {
+ Objects.requireNonNull(partialQuery);
+ if (!mEgressingData) {
+ Slog.v(TAG, "Query should not be egressed within the unattention state.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot stream queries without attention signals."));
+ return;
+ }
+ mQueryStreaming = true;
+ callback.onQueryDetected(partialQuery);
+ Slog.i(TAG, "Egressed from visual query detection process.");
}
- mQueryStreaming = true;
- callback.onQueryDetected(partialQuery);
- Slog.i(TAG, "Egressed from visual query detection process.");
}
@Override
public void onQueryFinished() throws RemoteException {
Slog.v(TAG, "BinderCallback#onQueryFinished");
- if (!mQueryStreaming) {
- Slog.v(TAG, "Query streaming state signal FINISHED is block since there is"
- + " no active query being streamed.");
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_STREAMING_STATE,
- "Cannot send FINISHED signal with no query streamed."));
- return;
+ synchronized (mLock) {
+ if (!mQueryStreaming) {
+ Slog.v(TAG, "Query streaming state signal FINISHED is block since there is"
+ + " no active query being streamed.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot send FINISHED signal with no query streamed."));
+ return;
+ }
+ callback.onQueryFinished();
+ mQueryStreaming = false;
}
- callback.onQueryFinished();
- mQueryStreaming = false;
}
@Override
public void onQueryRejected() throws RemoteException {
Slog.v(TAG, "BinderCallback#onQueryRejected");
- if (!mQueryStreaming) {
- Slog.v(TAG, "Query streaming state signal REJECTED is block since there is"
- + " no active query being streamed.");
- callback.onVisualQueryDetectionServiceFailure(
- new VisualQueryDetectionServiceFailure(
- ERROR_CODE_ILLEGAL_STREAMING_STATE,
- "Cannot send REJECTED signal with no query streamed."));
- return;
+ synchronized (mLock) {
+ if (!mQueryStreaming) {
+ Slog.v(TAG, "Query streaming state signal REJECTED is block since there is"
+ + " no active query being streamed.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot send REJECTED signal with no query streamed."));
+ return;
+ }
+ callback.onQueryRejected();
+ mQueryStreaming = false;
}
- callback.onQueryRejected();
- mQueryStreaming = false;
}
};
return mRemoteDetectionService.run(
diff --git a/telecomm/java/android/telecom/AuthenticatorService.java b/telecomm/java/android/telecom/AuthenticatorService.java
deleted file mode 100644
index 1e43c71..0000000
--- a/telecomm/java/android/telecom/AuthenticatorService.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.telecom;
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.NetworkErrorException;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-
-/**
- * A generic stub account authenticator service often used for sync adapters that do not directly
- * involve accounts.
- *
- * @hide
- */
-public class AuthenticatorService extends Service {
- private static Authenticator mAuthenticator;
-
- @Override
- public void onCreate() {
- mAuthenticator = new Authenticator(this);
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mAuthenticator.getIBinder();
- }
-
- /**
- * Stub account authenticator. All methods either return null or throw an exception.
- */
- public class Authenticator extends AbstractAccountAuthenticator {
- public Authenticator(Context context) {
- super(context);
- }
-
- @Override
- public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
- String s) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse,
- String s, String s2, String[] strings, Bundle bundle)
- throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, Bundle bundle)
- throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, String s, Bundle bundle)
- throws NetworkErrorException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getAuthTokenLabel(String s) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, String s, Bundle bundle)
- throws NetworkErrorException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
- Account account, String[] strings)
- throws NetworkErrorException {
- throw new UnsupportedOperationException();
- }
- }
-}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index def52a5..874c10c 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -210,100 +210,6 @@
"android.telecom.extra.SILENT_RINGING_REQUESTED";
/**
- * Call event sent from a {@link Call} via {@link #sendCallEvent(String, Bundle)} to inform
- * Telecom that the user has requested that the current {@link Call} should be handed over
- * to another {@link ConnectionService}.
- * <p>
- * The caller must specify the {@link #EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE} to indicate to
- * Telecom which {@link PhoneAccountHandle} the {@link Call} should be handed over to.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EVENT_REQUEST_HANDOVER =
- "android.telecom.event.REQUEST_HANDOVER";
-
- /**
- * Extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event. Specifies the
- * {@link PhoneAccountHandle} to which a call should be handed over to.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE =
- "android.telecom.extra.HANDOVER_PHONE_ACCOUNT_HANDLE";
-
- /**
- * Integer extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event. Specifies the
- * video state of the call when it is handed over to the new {@link PhoneAccount}.
- * <p>
- * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
- * {@link VideoProfile#STATE_BIDIRECTIONAL}, {@link VideoProfile#STATE_RX_ENABLED}, and
- * {@link VideoProfile#STATE_TX_ENABLED}.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EXTRA_HANDOVER_VIDEO_STATE =
- "android.telecom.extra.HANDOVER_VIDEO_STATE";
-
- /**
- * Extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event. Used by the
- * {@link InCallService} initiating a handover to provide a {@link Bundle} with extra
- * information to the handover {@link ConnectionService} specified by
- * {@link #EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE}.
- * <p>
- * This {@link Bundle} is not interpreted by Telecom, but passed as-is to the
- * {@link ConnectionService} via the request extras when
- * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}
- * is called to initate the handover.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EXTRA_HANDOVER_EXTRAS = "android.telecom.extra.HANDOVER_EXTRAS";
-
- /**
- * Call event sent from Telecom to the handover {@link ConnectionService} via
- * {@link Connection#onCallEvent(String, Bundle)} to inform a {@link Connection} that a handover
- * to the {@link ConnectionService} has completed successfully.
- * <p>
- * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EVENT_HANDOVER_COMPLETE =
- "android.telecom.event.HANDOVER_COMPLETE";
-
- /**
- * Call event sent from Telecom to the handover destination {@link ConnectionService} via
- * {@link Connection#onCallEvent(String, Bundle)} to inform the handover destination that the
- * source connection has disconnected. The {@link Bundle} parameter for the call event will be
- * {@code null}.
- * <p>
- * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EVENT_HANDOVER_SOURCE_DISCONNECTED =
- "android.telecom.event.HANDOVER_SOURCE_DISCONNECTED";
-
- /**
- * Call event sent from Telecom to the handover {@link ConnectionService} via
- * {@link Connection#onCallEvent(String, Bundle)} to inform a {@link Connection} that a handover
- * to the {@link ConnectionService} has failed.
- * <p>
- * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EVENT_HANDOVER_FAILED =
- "android.telecom.event.HANDOVER_FAILED";
-
- /**
* Event reported from the Telecom stack to report an in-call diagnostic message which the
* dialer app may opt to display to the user. A diagnostic message is used to communicate
* scenarios the device has detected which may impact the quality of the ongoing call.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4a541da..a2105b0 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -961,28 +961,6 @@
"android.telecom.event.CALL_REMOTELY_UNHELD";
/**
- * Connection event used to inform an {@link InCallService} which initiated a call handover via
- * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has
- * successfully completed.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EVENT_HANDOVER_COMPLETE =
- "android.telecom.event.HANDOVER_COMPLETE";
-
- /**
- * Connection event used to inform an {@link InCallService} which initiated a call handover via
- * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has failed
- * to complete.
- * @hide
- * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
- * APIs instead.
- */
- public static final String EVENT_HANDOVER_FAILED =
- "android.telecom.event.HANDOVER_FAILED";
-
- /**
* String Connection extra key used to store SIP invite fields for an incoming call for IMS call
*/
public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
@@ -3362,15 +3340,6 @@
public void onDisconnect() {}
/**
- * Notifies this Connection of a request to disconnect a participant of the conference managed
- * by the connection.
- *
- * @param endpoint the {@link Uri} of the participant to disconnect.
- * @hide
- */
- public void onDisconnectConferenceParticipant(Uri endpoint) {}
-
- /**
* Notifies this Connection of a request to separate from its parent conference.
*/
public void onSeparate() {}
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index db38f88..575ec27 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -187,14 +187,15 @@
}
for (ResolveInfo r : packages) {
- if (r.activityInfo == null
- || pm.checkPermission(
+ if (r.activityInfo == null) {
+ Rlog.w(TAG, "Found package without activity");
+ continue;
+ } else if (pm.checkPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
r.activityInfo.packageName)
- != PackageManager.PERMISSION_GRANTED) {
- Rlog.w(TAG,
- "Found package without proper permissions or no activity"
- + r.activityInfo.packageName);
+ != PackageManager.PERMISSION_GRANTED) {
+ Rlog.w(TAG, "Found package without proper permissions"
+ + r.activityInfo.packageName);
continue;
}
Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bcd9929..c7b84a3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -525,6 +525,12 @@
public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
/**
+ * Used in the Preferred Network Types menu to determine if the 3G option is displayed.
+ */
+ @FlaggedApi(Flags.FLAG_HIDE_PREFER_3G_ITEM)
+ public static final String KEY_PREFER_3G_VISIBILITY_BOOL = "prefer_3g_visibility_bool";
+
+ /**
* Used in Cellular Network Settings for preferred network type to show 4G only mode.
* @hide
*/
@@ -3715,19 +3721,19 @@
* This configuration allows the system UI to display different 5G icons for different 5G
* scenarios.
*
- * There are five 5G scenarios:
- * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
- * millimeter wave.
- * 2. connected: device currently connected to 5G cell as the secondary cell but not using
- * millimeter wave.
- * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability(not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in IDLE state.
- * 4. not_restricted_rrc_con: device camped on a network that has 5G capability(not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in CONNECTED state.
- * 5. restricted: device camped on a network that has 5G capability(not necessary to connect a
- * 5G cell as a secondary cell) but the use of 5G is restricted.
+ * There are six 5G scenarios for icon configuration:
+ * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+ * and considered NR advanced.
+ * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+ * considered NR advanced.
+ * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+ * and RRC currently in IDLE state.
+ * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in IDLE state.
+ * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in CONNECTED state.
+ * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+ * restricted.
*
* The configured string contains multiple key-value pairs separated by comma. For each pair,
* the key and value are separated by a colon. The key corresponds to a 5G status above and
@@ -3748,21 +3754,21 @@
* This configuration allows the system UI to determine how long to continue to display 5G icons
* when the device switches between different 5G scenarios.
*
- * There are seven 5G scenarios:
- * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
- * millimeter wave.
- * 2. connected: device currently connected to 5G cell as the secondary cell but not using
- * millimeter wave.
- * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in IDLE state.
- * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in CONNECTED state.
- * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a
- * 5G cell as a secondary cell) but the use of 5G is restricted.
- * 6. legacy: device is not camped on a network that has 5G capability
- * 7. any: any of the above scenarios
+ * There are eight 5G scenarios:
+ * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+ * and considered NR advanced.
+ * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+ * considered NR advanced.
+ * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+ * and RRC currently in IDLE state.
+ * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in IDLE state.
+ * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in CONNECTED state.
+ * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+ * restricted.
+ * 7. legacy: device is not camped on a network that has 5G capability
+ * 8. any: any of the above scenarios
*
* The configured string contains various timer rules separated by a semicolon.
* Each rule will have three items: prior 5G scenario, current 5G scenario, and grace period
@@ -3770,8 +3776,8 @@
* 5G scenario, the system UI will continue to show the icon for the prior 5G scenario (defined
* in {@link #KEY_5G_ICON_CONFIGURATION_STRING}) for the amount of time specified by the grace
* period. If the prior 5G scenario is reestablished, the timer will reset and start again if
- * the UE changes 5G scenarios again. Defined states (5G scenarios #1-5) take precedence over
- * 'any' (5G scenario #6), and unspecified transitions have a default grace period of 0.
+ * the UE changes 5G scenarios again. Defined states (5G scenarios #1-7) take precedence over
+ * 'any' (5G scenario #8), and unspecified transitions have a default grace period of 0.
* The order of rules in the configuration determines the priority (the first applicable timer
* rule will be used).
*
@@ -3794,21 +3800,21 @@
* This configuration extends {@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING} to allow the
* system UI to continue displaying 5G icons after the initial timer expires.
*
- * There are seven 5G scenarios:
- * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
- * millimeter wave.
- * 2. connected: device currently connected to 5G cell as the secondary cell but not using
- * millimeter wave.
- * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in IDLE state.
- * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary
- * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
- * currently in CONNECTED state.
- * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a
- * 5G cell as a secondary cell) but the use of 5G is restricted.
- * 6. legacy: device is not camped on a network that has 5G capability
- * 7. any: any of the above scenarios
+ * There are eight 5G scenarios:
+ * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+ * and considered NR advanced.
+ * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+ * considered NR advanced.
+ * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+ * and RRC currently in IDLE state.
+ * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in IDLE state.
+ * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+ * 5G is not restricted and RRC currently in CONNECTED state.
+ * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+ * restricted.
+ * 7. legacy: device is not camped on a network that has 5G capability
+ * 8. any: any of the above scenarios
*
* The configured string contains various timer rules separated by a semicolon.
* Each rule will have three items: primary 5G scenario, secondary 5G scenario, and
@@ -3818,7 +3824,7 @@
* period. If the primary 5G scenario is reestablished, the timers will reset and the system UI
* will continue to display the icon for the primary 5G scenario without interruption. If the
* secondary 5G scenario is lost, the timer will reset and the icon will reflect the true state.
- * Defined states (5G scenarios #1-5) take precedence over 'any' (5G scenario #6), and
+ * Defined states (5G scenarios #1-7) take precedence over 'any' (5G scenario #8), and
* unspecified transitions have a default grace period of 0. The order of rules in the
* configuration determines the priority (the first applicable timer rule will be used).
*
@@ -8885,18 +8891,18 @@
KEY_PREFIX + "epdg_static_address_roaming_string";
/**
- * Controls if the multiple SA proposals allowed for IKE session to include
- * all the 3GPP TS 33.210 and RFC 8221 supported cipher suites in multiple
- * IKE SA proposals as per RFC 7296.
+ * Enables the use of multiple IKE SA proposals, encompassing both carrier-preferred
+ * ciphers and all supported ciphers from 3GPP TS 33.210 and RFC 8221,
+ * as defined in RFC 7296.
*/
@FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS)
public static final String KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL =
KEY_PREFIX + "supports_ike_session_multiple_sa_proposals_bool";
/**
- * Controls if the multiple SA proposals allowed for Child session to include
- * all the 3GPP TS 33.210 and RFC 8221 supported cipher suites in multiple
- * Child SA proposals as per RFC 7296.
+ * Enables the use of multiple Child SA proposals, encompassing both carrier-preferred
+ * ciphers and all supported ciphers from 3GPP TS 33.210 and RFC 8221,
+ * as defined in RFC 7296.
*/
@FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS)
public static final String KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL =
@@ -10044,6 +10050,49 @@
public static final String KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE =
"auto_data_switch_rat_signal_score_string_bundle";
+ // TODO(b/316183370): replace @code with @link in javadoc after feature is released
+ /**
+ * An array of cellular services supported by a subscription.
+ *
+ * <p>Permissible values include:
+ * <ul>
+ * <li>{@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} for voice services</li>
+ * <li>{@code SubscriptionManager#SERVICE_CAPABILITY_SMS} for SMS services</li>
+ * <li>{@code SubscriptionManager#SERVICE_CAPABILITY_DATA} for data services</li>
+ * </ul>
+ *
+ * <p>Carrier-specific factors may influence how these services are supported. Therefore,
+ * modifying this carrier configuration might not always enable the specified services. These
+ * capability bitmasks should be considered as indicators of a carrier's preferred services
+ * to enhance user experience, rather than as absolute platform guarantees.
+ *
+ * <p>Device-level service capabilities, defined by
+ * {@code TelephonyManager#isDeviceVoiceCapable} and
+ * {@code TelephonyManager#isDeviceSmsCapable}, take precedence over these subscription-level
+ * settings. For instance, a device where {@code TelephonyManager#isDeviceVoiceCapable} returns
+ * false may not be able to make voice calls, even if subscribed to a service marked as
+ * voice-capable.
+ *
+ * <p>To determine a subscription's cellular service capabilities, use
+ * {@code SubscriptionInfo#getServiceCapabilities()}. To track changes in services, register
+ * a {@link SubscriptionManager.OnSubscriptionsChangedListener} and invoke the
+ * same method in its callback.
+ *
+ * <p>Emergency service availability may not depend on the cellular service capabilities.
+ * For example, emergency calls might be possible on a subscription even if it lacks
+ * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE}.
+ *
+ * <p>If unset, the default value is “[1, 2, 3]” (supports all cellular services).
+ *
+ * @see TelephonyManager#isDeviceVoiceCapable
+ * @see TelephonyManager#isDeviceSmsCapable
+ * @see SubscriptionInfo#getServiceCapabilities()
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY =
+ "cellular_service_capabilities_int_array";
+
/** The default value for every variable. */
private static final PersistableBundle sDefaults;
@@ -10144,6 +10193,7 @@
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
sDefaults.putBoolean(KEY_PREFER_2G_BOOL, false);
+ sDefaults.putBoolean(KEY_PREFER_3G_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_4G_ONLY_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
@@ -10558,7 +10608,7 @@
sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false);
sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
- "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G,"
+ "connected_mmwave:5G,connected:5G,connected_rrc_idle:5G,not_restricted_rrc_idle:5G,"
+ "not_restricted_rrc_con:5G");
sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
@@ -10830,6 +10880,7 @@
new boolean[] {false, false, true, false, false});
sDefaults.putStringArray(KEY_CARRIER_SERVICE_NAME_STRING_ARRAY, new String[0]);
sDefaults.putStringArray(KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY, new String[0]);
+ sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});
}
/**
diff --git a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
new file mode 100644
index 0000000..61d7ead
--- /dev/null
+++ b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
@@ -0,0 +1,214 @@
+/*
+ * 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A single occurrence capturing a notable change to previously reported
+ * cryptography algorithms for a given network and network event.
+ *
+ * @hide
+ */
+public final class SecurityAlgorithmUpdate implements Parcelable {
+ private static final String TAG = "SecurityAlgorithmUpdate";
+
+ private @ConnectionEvent int mConnectionEvent;
+ private @SecurityAlgorithm int mEncryption;
+ private @SecurityAlgorithm int mIntegrity;
+ private boolean mIsUnprotectedEmergency;
+
+ public SecurityAlgorithmUpdate(@ConnectionEvent int connectionEvent,
+ @SecurityAlgorithm int encryption, @SecurityAlgorithm int integrity,
+ boolean isUnprotectedEmergency) {
+ mConnectionEvent = connectionEvent;
+ mEncryption = encryption;
+ mIntegrity = integrity;
+ mIsUnprotectedEmergency = isUnprotectedEmergency;
+ }
+
+ private SecurityAlgorithmUpdate(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public @ConnectionEvent int getConnectionEvent() {
+ return mConnectionEvent;
+ }
+
+ public @SecurityAlgorithm int getEncryption() {
+ return mEncryption;
+ }
+
+ public @SecurityAlgorithm int getIntegrity() {
+ return mIntegrity;
+ }
+
+ public boolean isUnprotectedEmergency() {
+ return mIsUnprotectedEmergency;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mConnectionEvent);
+ out.writeInt(mEncryption);
+ out.writeInt(mIntegrity);
+ out.writeBoolean(mIsUnprotectedEmergency);
+ }
+
+ private void readFromParcel(@NonNull Parcel in) {
+ mConnectionEvent = in.readInt();
+ mEncryption = in.readInt();
+ mIntegrity = in.readInt();
+ mIsUnprotectedEmergency = in.readBoolean();
+ }
+
+ public static final Parcelable.Creator<SecurityAlgorithmUpdate> CREATOR =
+ new Parcelable.Creator<SecurityAlgorithmUpdate>() {
+ public SecurityAlgorithmUpdate createFromParcel(Parcel in) {
+ return new SecurityAlgorithmUpdate(in);
+ }
+
+ public SecurityAlgorithmUpdate[] newArray(int size) {
+ return new SecurityAlgorithmUpdate[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return TAG + ":{ mConnectionEvent = " + mConnectionEvent + " mEncryption = " + mEncryption
+ + " mIntegrity = " + mIntegrity + " mIsUnprotectedEmergency = "
+ + mIsUnprotectedEmergency;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SecurityAlgorithmUpdate)) return false;
+ SecurityAlgorithmUpdate that = (SecurityAlgorithmUpdate) o;
+ return mConnectionEvent == that.mConnectionEvent
+ && mEncryption == that.mEncryption
+ && mIntegrity == that.mIntegrity
+ && mIsUnprotectedEmergency == that.mIsUnprotectedEmergency;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConnectionEvent, mEncryption, mIntegrity, mIsUnprotectedEmergency);
+ }
+
+ public static final int CONNECTION_EVENT_CS_SIGNALLING_GSM = 0;
+ public static final int CONNECTION_EVENT_PS_SIGNALLING_GPRS = 1;
+ public static final int CONNECTION_EVENT_CS_SIGNALLING_3G = 2;
+ public static final int CONNECTION_EVENT_PS_SIGNALLING_3G = 3;
+ public static final int CONNECTION_EVENT_NAS_SIGNALLING_LTE = 4;
+ public static final int CONNECTION_EVENT_AS_SIGNALLING_LTE = 5;
+ public static final int CONNECTION_EVENT_VOLTE_SIP = 6;
+ public static final int CONNECTION_EVENT_VOLTE_RTP = 7;
+ public static final int CONNECTION_EVENT_NAS_SIGNALLING_5G = 8;
+ public static final int CONNECTION_EVENT_AS_SIGNALLING_5G = 9;
+ public static final int CONNECTION_EVENT_VONR_SIP = 10;
+ public static final int CONNECTION_EVENT_VONR_RTP = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {CONNECTION_EVENT_CS_SIGNALLING_GSM,
+ CONNECTION_EVENT_PS_SIGNALLING_GPRS, CONNECTION_EVENT_CS_SIGNALLING_3G,
+ CONNECTION_EVENT_PS_SIGNALLING_3G, CONNECTION_EVENT_NAS_SIGNALLING_LTE,
+ CONNECTION_EVENT_AS_SIGNALLING_LTE, CONNECTION_EVENT_VOLTE_SIP,
+ CONNECTION_EVENT_VOLTE_RTP, CONNECTION_EVENT_NAS_SIGNALLING_5G,
+ CONNECTION_EVENT_AS_SIGNALLING_5G, CONNECTION_EVENT_VONR_SIP,
+ CONNECTION_EVENT_VONR_RTP})
+ public @interface ConnectionEvent {
+ }
+
+ public static final int SECURITY_ALGORITHM_A50 = 0;
+ public static final int SECURITY_ALGORITHM_A51 = 1;
+ public static final int SECURITY_ALGORITHM_A52 = 2;
+ public static final int SECURITY_ALGORITHM_A53 = 3;
+ public static final int SECURITY_ALGORITHM_A54 = 4;
+ public static final int SECURITY_ALGORITHM_GEA0 = 14;
+ public static final int SECURITY_ALGORITHM_GEA1 = 15;
+ public static final int SECURITY_ALGORITHM_GEA2 = 16;
+ public static final int SECURITY_ALGORITHM_GEA3 = 17;
+ public static final int SECURITY_ALGORITHM_GEA4 = 18;
+ public static final int SECURITY_ALGORITHM_GEA5 = 19;
+ public static final int SECURITY_ALGORITHM_UEA0 = 29;
+ public static final int SECURITY_ALGORITHM_UEA1 = 30;
+ public static final int SECURITY_ALGORITHM_UEA2 = 31;
+ public static final int SECURITY_ALGORITHM_EEA0 = 41;
+ public static final int SECURITY_ALGORITHM_EEA1 = 42;
+ public static final int SECURITY_ALGORITHM_EEA2 = 43;
+ public static final int SECURITY_ALGORITHM_EEA3 = 44;
+ public static final int SECURITY_ALGORITHM_NEA0 = 55;
+ public static final int SECURITY_ALGORITHM_NEA1 = 56;
+ public static final int SECURITY_ALGORITHM_NEA2 = 57;
+ public static final int SECURITY_ALGORITHM_NEA3 = 58;
+ public static final int SECURITY_ALGORITHM_SIP_NULL = 68;
+ public static final int SECURITY_ALGORITHM_AES_GCM = 69;
+ public static final int SECURITY_ALGORITHM_AES_GMAC = 70;
+ public static final int SECURITY_ALGORITHM_AES_CBC = 71;
+ public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72;
+ public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73;
+ public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74;
+ public static final int SECURITY_ALGORITHM_HMAC_SHA1_96_NULL = 75;
+ public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 76;
+ public static final int SECURITY_ALGORITHM_HMAC_MD5_96_NULL = 77;
+ public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87;
+ public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88;
+ public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89;
+ public static final int SECURITY_ALGORITHM_ENCR_AES_GCM_16 = 99;
+ public static final int SECURITY_ALGORITHM_ENCR_AES_CBC = 100;
+ public static final int SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128 = 101;
+ public static final int SECURITY_ALGORITHM_UNKNOWN = 113;
+ public static final int SECURITY_ALGORITHM_OTHER = 114;
+ public static final int SECURITY_ALGORITHM_ORYX = 124;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {SECURITY_ALGORITHM_A50, SECURITY_ALGORITHM_A51,
+ SECURITY_ALGORITHM_A52, SECURITY_ALGORITHM_A53,
+ SECURITY_ALGORITHM_A54, SECURITY_ALGORITHM_GEA0, SECURITY_ALGORITHM_GEA1,
+ SECURITY_ALGORITHM_GEA2, SECURITY_ALGORITHM_GEA3, SECURITY_ALGORITHM_GEA4,
+ SECURITY_ALGORITHM_GEA5, SECURITY_ALGORITHM_UEA0, SECURITY_ALGORITHM_UEA1,
+ SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1,
+ SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0,
+ SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3,
+ SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
+ SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC,
+ SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC,
+ SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_SHA1_96_NULL,
+ SECURITY_ALGORITHM_HMAC_MD5_96, SECURITY_ALGORITHM_HMAC_MD5_96_NULL,
+ SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8,
+ SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16,
+ SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
+ SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
+ public @interface SecurityAlgorithm {
+ }
+
+}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 6ebf3be..a188581 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -54,6 +54,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* A Parcelable class for Subscription Information.
@@ -262,6 +263,11 @@
private final boolean mIsOnlyNonTerrestrialNetwork;
/**
+ * The service capabilities (in the form of bitmask combination) the subscription supports.
+ */
+ private final int mServiceCapabilities;
+
+ /**
* @hide
*
* @deprecated Use {@link SubscriptionInfo.Builder}.
@@ -386,6 +392,7 @@
this.mPortIndex = portIndex;
this.mUsageSetting = usageSetting;
this.mIsOnlyNonTerrestrialNetwork = false;
+ this.mServiceCapabilities = 0;
}
/**
@@ -425,6 +432,7 @@
this.mPortIndex = builder.mPortIndex;
this.mUsageSetting = builder.mUsageSetting;
this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork;
+ this.mServiceCapabilities = builder.mServiceCapabilities;
}
/**
@@ -882,6 +890,44 @@
return mIsOnlyNonTerrestrialNetwork;
}
+ // TODO(b/316183370): replace @code with @link in javadoc after feature is released
+ /**
+ * Retrieves the service capabilities for the current subscription.
+ *
+ * <p>These capabilities are hint to system components and applications, allowing them to
+ * enhance user experience. For instance, a Dialer application can inform the user that the
+ * current subscription is incapable of making voice calls if the voice service is not
+ * available.
+ *
+ * <p>Correct usage of these service capabilities must also consider the device's overall
+ * service capabilities. For example, even if the subscription supports voice calls, a voice
+ * call might not be feasible on a device that only supports data services. To determine the
+ * device's capabilities for voice and SMS services, refer to
+ * {@code TelephonyManager#isDeviceVoiceCapable()} and
+ * {@code TelephonyManager#isDeviceSmsCapable()}.
+ *
+ * <p>Emergency service availability may not directly correlate with the subscription or
+ * device's general service capabilities. In some cases, emergency calls might be possible
+ * even if the subscription or device does not typically support voice services.
+ *
+ * @return A set of integer representing the subscription's service capabilities,
+ * defined by {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE},
+ * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS}
+ * and {@code SubscriptionManager#SERVICE_CAPABILITY_DATA}.
+ *
+ * @see TelephonyManager#isDeviceVoiceCapable()
+ * @see TelephonyManager#isDeviceSmsCapable()
+ * @see CarrierConfigManager#KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY
+ * @see SubscriptionManager#SERVICE_CAPABILITY_VOICE
+ * @see SubscriptionManager#SERVICE_CAPABILITY_SMS
+ * @see SubscriptionManager#SERVICE_CAPABILITY_DATA
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() {
+ return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities);
+ }
+
@NonNull
public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
new Parcelable.Creator<SubscriptionInfo>() {
@@ -919,6 +965,8 @@
.setUiccApplicationsEnabled(source.readBoolean())
.setUsageSetting(source.readInt())
.setOnlyNonTerrestrialNetwork(source.readBoolean())
+ .setServiceCapabilities(
+ SubscriptionManager.getServiceCapabilitiesSet(source.readInt()))
.build();
}
@@ -961,6 +1009,7 @@
dest.writeBoolean(mAreUiccApplicationsEnabled);
dest.writeInt(mUsageSetting);
dest.writeBoolean(mIsOnlyNonTerrestrialNetwork);
+ dest.writeInt(mServiceCapabilities);
}
@Override
@@ -1024,6 +1073,8 @@
+ " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
+ " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
+ + " serviceCapabilities=" + SubscriptionManager.getServiceCapabilitiesSet(
+ mServiceCapabilities).toString()
+ "]";
}
@@ -1049,7 +1100,8 @@
that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
&& mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
- && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork;
+ && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork
+ && mServiceCapabilities == that.mServiceCapabilities;
}
@Override
@@ -1058,7 +1110,7 @@
mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
- mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork);
+ mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork, mServiceCapabilities);
result = 31 * result + Arrays.hashCode(mEhplmns);
result = 31 * result + Arrays.hashCode(mHplmns);
result = 31 * result + Arrays.hashCode(mNativeAccessRules);
@@ -1263,6 +1315,11 @@
private boolean mIsOnlyNonTerrestrialNetwork = false;
/**
+ * Service capabilities bitmasks the subscription supports.
+ */
+ private int mServiceCapabilities = 0;
+
+ /**
* Default constructor.
*/
public Builder() {
@@ -1305,6 +1362,7 @@
mPortIndex = info.mPortIndex;
mUsageSetting = info.mUsageSetting;
mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork;
+ mServiceCapabilities = info.mServiceCapabilities;
}
/**
@@ -1703,6 +1761,32 @@
}
/**
+ * Set the service capabilities that the subscription supports.
+ *
+ * @param capabilities Bitmask combination of SubscriptionManager
+ * .SERVICE_CAPABILITY_XXX.
+ * @return The builder.
+ *
+ * @throws IllegalArgumentException when any capability is not supported.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public Builder setServiceCapabilities(
+ @NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) {
+ int combinedCapabilities = 0;
+ for (int capability : capabilities) {
+ if (capability < SubscriptionManager.SERVICE_CAPABILITY_VOICE
+ || capability > SubscriptionManager.SERVICE_CAPABILITY_MAX) {
+ throw new IllegalArgumentException(
+ "Invalid service capability value: " + capability);
+ }
+ combinedCapabilities |= SubscriptionManager.serviceCapabilityToBitmask(capability);
+ }
+ mServiceCapabilities = combinedCapabilities;
+ return this;
+ }
+
+ /**
* Build the {@link SubscriptionInfo}.
*
* @return The {@link SubscriptionInfo} instance.
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 326b6f5..6c8663a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -88,10 +88,12 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -1138,6 +1140,14 @@
*/
public static final String IS_NTN = SimInfo.COLUMN_IS_NTN;
+ /**
+ * TelephonyProvider column name to identify service capabilities.
+ * Disabled by default.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String SERVICE_CAPABILITIES = SimInfo.COLUMN_SERVICE_CAPABILITIES;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"USAGE_SETTING_"},
@@ -1347,6 +1357,86 @@
})
public @interface PhoneNumberSource {}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SERVICE_CAPABILITY"},
+ value = {
+ SERVICE_CAPABILITY_VOICE,
+ SERVICE_CAPABILITY_SMS,
+ SERVICE_CAPABILITY_DATA,
+ })
+ public @interface ServiceCapability {
+ }
+
+ /**
+ * Represents a value indicating the voice calling capabilities of a subscription.
+ *
+ * <p>This value identifies whether the subscription supports various voice calling services.
+ * These services can include circuit-switched (CS) calling, packet-switched (PS) IMS (IP
+ * Multimedia Subsystem) calling, and over-the-top (OTT) calling options.
+ *
+ * <p>Note: The availability of emergency calling services is not solely dependent on this
+ * voice capability. Emergency services may be accessible even if the subscription lacks
+ * standard voice capabilities. However, the device's ability to support emergency calls
+ * can be influenced by its inherent voice capabilities, as determined by
+ * {@link TelephonyManager#isDeviceVoiceCapable()}.
+ *
+ * @see TelephonyManager#isDeviceVoiceCapable()
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final int SERVICE_CAPABILITY_VOICE = 1;
+
+ /**
+ * Represents a value indicating the SMS capabilities of a subscription.
+ *
+ * <p>This value identifies whether the subscription supports various sms services.
+ * These services can include circuit-switched (CS) SMS, packet-switched (PS) IMS (IP
+ * Multimedia Subsystem) SMS, and over-the-top (OTT) SMS options.
+ *
+ * <p>Note: The availability of emergency SMS services is not solely dependent on this
+ * sms capability. Emergency services may be accessible even if the subscription lacks
+ * standard sms capabilities. However, the device's ability to support emergency sms
+ * can be influenced by its inherent sms capabilities, as determined by
+ * {@link TelephonyManager#isDeviceSmsCapable()}.
+ *
+ * @see TelephonyManager#isDeviceSmsCapable()
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final int SERVICE_CAPABILITY_SMS = 2;
+
+ /**
+ * Represents a value indicating the data calling capabilities of a subscription.
+ */
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public static final int SERVICE_CAPABILITY_DATA = 3;
+
+ /**
+ * Maximum value of service capabilities supported so far.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA;
+
+ /**
+ * Bitmask for {@code SERVICE_CAPABILITY_VOICE}.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_VOICE_BITMASK =
+ serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE);
+
+ /**
+ * Bitmask for {@code SERVICE_CAPABILITY_SMS}.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_SMS_BITMASK =
+ serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS);
+
+ /**
+ * Bitmask for {@code SERVICE_CAPABILITY_DATA}.
+ * @hide
+ */
+ public static final int SERVICE_CAPABILITY_DATA_BITMASK =
+ serviceCapabilityToBitmask(SERVICE_CAPABILITY_DATA);
+
private final Context mContext;
/**
@@ -4484,4 +4574,38 @@
}
return new ArrayList<>();
}
+
+ /**
+ * @return the bitmasks combination of all service capabilities.
+ * @hide
+ */
+ public static int getAllServiceCapabilityBitmasks() {
+ return SERVICE_CAPABILITY_VOICE_BITMASK | SERVICE_CAPABILITY_SMS_BITMASK
+ | SERVICE_CAPABILITY_DATA_BITMASK;
+ }
+
+ /**
+ * @return The set of service capability from a bitmask combined one.
+ * @hide
+ */
+ @NonNull
+ @ServiceCapability
+ public static Set<Integer> getServiceCapabilitiesSet(int combinedServiceCapabilities) {
+ Set<Integer> capabilities = new HashSet<>();
+ for (int i = SERVICE_CAPABILITY_VOICE; i <= SERVICE_CAPABILITY_MAX; i++) {
+ final int capabilityBitmask = serviceCapabilityToBitmask(i);
+ if ((combinedServiceCapabilities & capabilityBitmask) == capabilityBitmask) {
+ capabilities.add(i);
+ }
+ }
+ return Collections.unmodifiableSet(capabilities);
+ }
+
+ /**
+ * @return The service capability bitmask from a {@link ServiceCapability} value.
+ * @hide
+ */
+ public static int serviceCapabilityToBitmask(@ServiceCapability int capability) {
+ return 1 << (capability - 1);
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f8166e5..9e292be 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2115,6 +2115,20 @@
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
"com.android.omadm.service.CONFIGURATION_UPDATE";
+ /**
+ * Activity action: Show setting to reset mobile networks.
+ *
+ * <p>On devices with a settings activity to reset mobile networks, the activity should be
+ * launched without additional permissions.
+ *
+ * <p>On some devices, this settings activity may not exist. Callers should ensure that this
+ * case is appropriately handled.
+ */
+ @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS)
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS =
+ "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
+
//
//
// Device Info
@@ -6683,8 +6697,10 @@
* 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 {@link #isDeviceVoiceCapable()}
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+ @Deprecated
public boolean isVoiceCapable() {
if (mContext == null) return true;
return mContext.getResources().getBoolean(
@@ -6692,6 +6708,30 @@
}
/**
+ * @return true if the current device is "voice capable".
+ * <p>
+ * "Voice capable" means that this device supports circuit-switched or IMS packet switched
+ * (i.e. voice) phone calls over the telephony network, and is allowed to display the in-call
+ * UI while a cellular voice call is active. This will be false on "data only" devices which
+ * can't make voice calls and don't support any in-call UI.
+ * <p>
+ * Note: the meaning of this flag is subtly different from the PackageManager
+ * .FEATURE_TELEPHONY system feature, which is available on any device with a telephony
+ * radio, even if the device is data-only.
+ * <p>
+ * To check if a subscription is "voice capable", call method
+ * {@link SubscriptionInfo#getServiceCapabilities()} and compare the result with
+ * bitmask {@link SubscriptionManager#SERVICE_CAPABILITY_VOICE}.
+ *
+ * @see SubscriptionInfo#getServiceCapabilities()
+ */
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public boolean isDeviceVoiceCapable() {
+ return isVoiceCapable();
+ }
+
+ /**
* @return true if the current device supports sms service.
* <p>
* If true, this means that the device supports both sending and
@@ -6699,6 +6739,7 @@
* <p>
* Note: Voicemail waiting sms, cell broadcasting sms, and MMS are
* disabled when device doesn't support sms.
+ * @deprecated Replaced by {@link #isDeviceSmsCapable()}
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public boolean isSmsCapable() {
@@ -6708,6 +6749,27 @@
}
/**
+ * @return true if the current device supports SMS service.
+ * <p>
+ * If true, this means that the device supports both sending and
+ * receiving SMS via the telephony network.
+ * <p>
+ * Note: Voicemail waiting SMS, cell broadcasting SMS, and MMS are
+ * disabled when device doesn't support SMS.
+ * <p>
+ * To check if a subscription is "SMS capable", call method
+ * {@link SubscriptionInfo#getServiceCapabilities()} and compare result with
+ * bitmask {@link SubscriptionManager#SERVICE_CAPABILITY_SMS}.
+ *
+ * @see SubscriptionInfo#getServiceCapabilities()
+ */
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+ @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+ public boolean isDeviceSmsCapable() {
+ return isSmsCapable();
+ }
+
+ /**
* Requests all available cell information from all radios on the device including the
* camped/registered, serving, and neighboring cells.
*
diff --git a/tests/ActivityManagerPerfTests/tests/Android.bp b/tests/ActivityManagerPerfTests/tests/Android.bp
index e5813ae..cce40f3a 100644
--- a/tests/ActivityManagerPerfTests/tests/Android.bp
+++ b/tests/ActivityManagerPerfTests/tests/Android.bp
@@ -30,6 +30,12 @@
"ActivityManagerPerfTestsUtils",
"collector-device-lib-platform",
],
+ data: [
+ ":ActivityManagerPerfTestsTestApp",
+ ":ActivityManagerPerfTestsStubApp1",
+ ":ActivityManagerPerfTestsStubApp2",
+ ":ActivityManagerPerfTestsStubApp3",
+ ],
platform_apis: true,
min_sdk_version: "25",
// For android.permission.FORCE_STOP_PACKAGES permission
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index c49f8fe..be47ec7 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -19,12 +19,12 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.traces.parsers.toFlickerComponent
-import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -58,9 +58,10 @@
}
transitions {
broadcastActionTrigger.doAction(ACTION_FINISH_ACTIVITY)
- wmHelper.StateSyncBuilder()
- .withActivityRemoved(ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent())
- .waitForAndVerify()
+ wmHelper
+ .StateSyncBuilder()
+ .withActivityRemoved(ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent())
+ .waitForAndVerify()
}
teardown { simpleApp.exit(wmHelper) }
}
@@ -69,10 +70,16 @@
@Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible()
- @FlakyTest(bugId = 246284124)
+ @Presubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS.toMutableList().also {
+ it.add(simpleApp.componentMatcher)
+ }
+ )
+ }
}
@Presubmit
diff --git a/tests/testables/src/android/testing/TestWithLooperRule.java b/tests/testables/src/android/testing/TestWithLooperRule.java
index 99b303e..37b39c3 100644
--- a/tests/testables/src/android/testing/TestWithLooperRule.java
+++ b/tests/testables/src/android/testing/TestWithLooperRule.java
@@ -19,7 +19,6 @@
import android.testing.TestableLooper.LooperFrameworkMethod;
import android.testing.TestableLooper.RunWithLooper;
-import org.junit.internal.runners.statements.InvokeMethod;
import org.junit.rules.MethodRule;
import org.junit.runner.RunWith;
import org.junit.runners.model.FrameworkMethod;
@@ -79,13 +78,11 @@
while (next != null) {
switch (next.getClass().getSimpleName()) {
case "RunAfters":
- this.<List<FrameworkMethod>>wrapFieldMethodFor(next,
- next.getClass(), "afters", method, target);
+ this.wrapFieldMethodFor(next, "afters", method, target);
next = getNextStatement(next, "next");
break;
case "RunBefores":
- this.<List<FrameworkMethod>>wrapFieldMethodFor(next,
- next.getClass(), "befores", method, target);
+ this.wrapFieldMethodFor(next, "befores", method, target);
next = getNextStatement(next, "next");
break;
case "FailOnTimeout":
@@ -95,8 +92,10 @@
next = getNextStatement(next, "originalStatement");
break;
case "InvokeMethod":
- this.<FrameworkMethod>wrapFieldMethodFor(next,
- InvokeMethod.class, "testMethod", method, target);
+ this.wrapFieldMethodFor(next, "testMethod", method, target);
+ return;
+ case "InvokeParameterizedMethod":
+ this.wrapFieldMethodFor(next, "frameworkMethod", method, target);
return;
default:
throw new Exception(
@@ -112,12 +111,11 @@
// Wrapping the befores, afters, and InvokeMethods with LooperFrameworkMethod
// within the statement.
- private <T> void wrapFieldMethodFor(Statement base, Class<?> targetClass, String fieldStr,
- FrameworkMethod method, Object target)
- throws NoSuchFieldException, IllegalAccessException {
- Field field = targetClass.getDeclaredField(fieldStr);
+ private void wrapFieldMethodFor(Statement base, String fieldStr, FrameworkMethod method,
+ Object target) throws NoSuchFieldException, IllegalAccessException {
+ Field field = base.getClass().getDeclaredField(fieldStr);
field.setAccessible(true);
- T fieldInstance = (T) field.get(base);
+ Object fieldInstance = field.get(base);
if (fieldInstance instanceof FrameworkMethod) {
field.set(base, looperWrap(method, target, (FrameworkMethod) fieldInstance));
} else {
diff --git a/tools/hoststubgen/README.md b/tools/hoststubgen/README.md
index 3455b0a..1a895dc 100644
--- a/tools/hoststubgen/README.md
+++ b/tools/hoststubgen/README.md
@@ -34,11 +34,6 @@
- `test-tiny-framework/` See `README.md` in it.
- - `test-framework`
- This directory was used during the prototype phase, but now that we have real ravenwood tests,
- this directory is obsolete and should be deleted.
-
-
- `scripts`
- `dump-jar.sh`
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 4eac361..57bcc04 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -9,7 +9,7 @@
// Visibility only for ravenwood prototype uses.
genrule_defaults {
- name: "hoststubgen-for-prototype-only-genrule",
+ name: "ravenwood-internal-only-visibility-genrule",
visibility: [
":__subpackages__",
"//frameworks/base",
@@ -19,7 +19,7 @@
// Visibility only for ravenwood prototype uses.
java_defaults {
- name: "hoststubgen-for-prototype-only-java",
+ name: "ravenwood-internal-only-visibility-java",
visibility: [
":__subpackages__",
"//frameworks/base",
@@ -29,7 +29,7 @@
// Visibility only for ravenwood prototype uses.
filegroup_defaults {
- name: "hoststubgen-for-prototype-only-filegroup",
+ name: "ravenwood-internal-only-visibility-filegroup",
visibility: [
":__subpackages__",
"//frameworks/base",
@@ -41,7 +41,7 @@
// This is only for the prototype. The productionized version is "ravenwood-annotations".
java_library {
name: "hoststubgen-annotations",
- defaults: ["hoststubgen-for-prototype-only-java"],
+ defaults: ["ravenwood-internal-only-visibility-java"],
srcs: [
"annotations-src/**/*.java",
],
@@ -115,7 +115,7 @@
// This is only for the prototype. The productionized version is "ravenwood-standard-options".
filegroup {
name: "hoststubgen-standard-options",
- defaults: ["hoststubgen-for-prototype-only-filegroup"],
+ defaults: ["ravenwood-internal-only-visibility-filegroup"],
srcs: [
"hoststubgen-standard-options.txt",
],
@@ -153,149 +153,25 @@
],
}
-// Generate the stub/impl from framework-all, with hidden APIs.
-java_genrule_host {
- name: "framework-all-hidden-api-host",
- defaults: ["hoststubgen-command-defaults"],
- cmd: hoststubgen_common_options +
- "--in-jar $(location :framework-all) " +
- "--policy-override-file $(location framework-policy-override.txt) ",
- srcs: [
- ":framework-all",
- "framework-policy-override.txt",
- ],
- visibility: ["//visibility:private"],
-}
-
-// Extract the stub jar from "framework-all-host" for subsequent build rules.
-// This is only for the prototype. Do not use it in "productionized" build rules.
-java_genrule_host {
- name: "framework-all-hidden-api-host-stub",
- defaults: ["hoststubgen-for-prototype-only-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":framework-all-hidden-api-host{host_stub.jar}",
- ],
- out: [
- "host_stub.jar",
- ],
-}
-
-// Extract the impl jar from "framework-all-host" for subsequent build rules.
-// This is only for the prototype. Do not use it in "productionized" build rules.
-java_genrule_host {
- name: "framework-all-hidden-api-host-impl",
- defaults: ["hoststubgen-for-prototype-only-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":framework-all-hidden-api-host{host_impl.jar}",
- ],
- out: [
- "host_impl.jar",
- ],
-}
-
-// Generate the stub/impl from framework-all, with only public/system/test APIs, without
-// hidden APIs.
-// This is only for the prototype. Do not use it in "productionized" build rules.
-java_genrule_host {
- name: "framework-all-test-api-host",
- defaults: ["hoststubgen-command-defaults"],
- cmd: hoststubgen_common_options +
- "--intersect-stub-jar $(location :android_test_stubs_current{.jar}) " +
- "--in-jar $(location :framework-all) " +
- "--policy-override-file $(location framework-policy-override.txt) ",
- srcs: [
- ":framework-all",
- ":android_test_stubs_current{.jar}",
- "framework-policy-override.txt",
- ],
- visibility: ["//visibility:private"],
-}
-
-// Extract the stub jar from "framework-all-test-api-host" for subsequent build rules.
-// This is only for the prototype. Do not use it in "productionized" build rules.
-java_genrule_host {
- name: "framework-all-test-api-host-stub",
- defaults: ["hoststubgen-for-prototype-only-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":framework-all-test-api-host{host_stub.jar}",
- ],
- out: [
- "host_stub.jar",
- ],
-}
-
-// Extract the impl jar from "framework-all-test-api-host" for subsequent build rules.
-// This is only for the prototype. Do not use it in "productionized" build rules.
-java_genrule_host {
- name: "framework-all-test-api-host-impl",
- defaults: ["hoststubgen-for-prototype-only-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":framework-all-test-api-host{host_impl.jar}",
- ],
- out: [
- "host_impl.jar",
- ],
-}
-
-// This library contains helper classes to build hostside tests/targets.
-// This essentially contains dependencies from tests that we can't actually use the real ones.
-// For example, the actual AndroidTestCase and AndroidJUnit4 don't run on the host side (yet),
-// so we pup "fake" implementations here.
-// Ideally this library should be empty.
-java_library_host {
- name: "hoststubgen-helper-framework-buildtime",
- defaults: ["hoststubgen-for-prototype-only-java"],
- srcs: [
- "helper-framework-buildtime-src/**/*.java",
- ],
- libs: [
- // We need it to pull in some of the framework classes used in this library,
- // such as Context.java.
- "framework-all-hidden-api-host-impl",
- "junit",
- ],
-}
-
-// This module contains "fake" libcore/dalvik classes, framework native substitution, etc,
-// that are needed at runtime.
-java_library_host {
- name: "hoststubgen-helper-framework-runtime",
- defaults: ["hoststubgen-for-prototype-only-java"],
- srcs: [
- "helper-framework-runtime-src/**/*.java",
- ],
- exclude_srcs: [
- "helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java",
- ],
- libs: [
- "hoststubgen-helper-runtime",
- "framework-all-hidden-api-host-impl",
- ],
-}
-
java_library_host {
name: "hoststubgen-helper-libcore-runtime",
- defaults: ["hoststubgen-for-prototype-only-java"],
srcs: [
"helper-framework-runtime-src/libcore-fake/**/*.java",
],
+ visibility: ["//visibility:private"],
}
java_host_for_device {
name: "hoststubgen-helper-libcore-runtime.ravenwood",
- defaults: ["hoststubgen-for-prototype-only-java"],
libs: [
"hoststubgen-helper-libcore-runtime",
],
+ visibility: ["//visibility:private"],
}
java_library {
name: "hoststubgen-helper-framework-runtime.ravenwood",
- defaults: ["hoststubgen-for-prototype-only-java"],
+ defaults: ["ravenwood-internal-only-visibility-java"],
srcs: [
"helper-framework-runtime-src/framework/**/*.java",
],
@@ -308,88 +184,3 @@
"hoststubgen-helper-libcore-runtime.ravenwood",
],
}
-
-// Defaults for host side test modules.
-// We need two rules for each test.
-// 1. A "-test-lib" jar, which compiles the test against the stub jar.
-// This one is only used by the second rule, so it should be "private.
-// 2. A "-test" jar, which includes 1 + the runtime (impl) jars.
-
-// This and next ones are for tests using framework-app, with hidden APIs.
-java_defaults {
- name: "hosttest-with-framework-all-hidden-api-test-lib-defaults",
- installable: false,
- libs: [
- "framework-all-hidden-api-host-stub",
- ],
- static_libs: [
- "hoststubgen-helper-framework-buildtime",
- "framework-annotations-lib",
- ],
- visibility: ["//visibility:private"],
-}
-
-// Default rules to include `libandroid_runtime`. For now, it's empty, but we'll use it
-// once we start using JNI.
-java_defaults {
- name: "hosttest-with-libandroid_runtime",
- jni_libs: [
- // "libandroid_runtime",
-
- // TODO: Figure out how to build them automatically.
- // Following ones are depended by libandroid_runtime.
- // Without listing them here, not only we won't get them under
- // $ANDROID_HOST_OUT/testcases/*/lib64, but also not under
- // $ANDROID_HOST_OUT/lib64, so we'd fail to load them at runtime.
- // ($ANDROID_HOST_OUT/lib/ gets all of them though.)
- // "libcutils",
- // "libharfbuzz_ng",
- // "libminikin",
- // "libz",
- // "libbinder",
- // "libhidlbase",
- // "libvintf",
- // "libicu",
- // "libutils",
- // "libtinyxml2",
- ],
-}
-
-java_defaults {
- name: "hosttest-with-framework-all-hidden-api-test-defaults",
- defaults: ["hosttest-with-libandroid_runtime"],
- installable: false,
- test_config: "AndroidTest-host.xml",
- static_libs: [
- "hoststubgen-helper-runtime",
- "hoststubgen-helper-framework-runtime",
- "framework-all-hidden-api-host-impl",
- ],
-}
-
-// This and next ones are for tests using framework-app, with public/system/test APIs,
-// without hidden APIs.
-java_defaults {
- name: "hosttest-with-framework-all-test-api-test-lib-defaults",
- installable: false,
- libs: [
- "framework-all-test-api-host-stub",
- ],
- static_libs: [
- "hoststubgen-helper-framework-buildtime",
- "framework-annotations-lib",
- ],
- visibility: ["//visibility:private"],
-}
-
-java_defaults {
- name: "hosttest-with-framework-all-test-api-test-defaults",
- defaults: ["hosttest-with-libandroid_runtime"],
- installable: false,
- test_config: "AndroidTest-host.xml",
- static_libs: [
- "hoststubgen-helper-runtime",
- "hoststubgen-helper-framework-runtime",
- "framework-all-test-api-host-impl",
- ],
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java
deleted file mode 100644
index e6d3866..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java
+++ /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 android.test;
-
-import android.content.Context;
-
-import junit.framework.TestCase;
-
-public class AndroidTestCase extends TestCase {
- protected Context mContext;
- public Context getContext() {
- throw new RuntimeException("[ravenwood] Class Context is not supported yet.");
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java
deleted file mode 100644
index 51c5d9a..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.annotation;
-
-// [ravenwood] TODO: Find the actual androidx jar containing it.s
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that a parameter, field or method return value can never be null.
- * <p>
- * This is a marker annotation and it has no specific attributes.
- *
- * @paramDoc This value cannot be {@code null}.
- * @returnDoc This value cannot be {@code null}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD})
-public @interface NonNull {
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java
deleted file mode 100644
index f1f0e8b..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.annotation;
-
-// [ravenwood] TODO: Find the actual androidx jar containing it.s
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that a parameter, field or method return value can be null.
- * <p>
- * When decorating a method call parameter, this denotes that the parameter can
- * legitimately be null and the method will gracefully deal with it. Typically
- * used on optional parameters.
- * <p>
- * When decorating a method, this denotes the method might legitimately return
- * null.
- * <p>
- * This is a marker annotation and it has no specific attributes.
- *
- * @paramDoc This value may be {@code null}.
- * @returnDoc This value may be {@code null}.
- * @hide
- */
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD})
-public @interface Nullable {
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java
deleted file mode 100644
index 0c82e4e..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java
+++ /dev/null
@@ -1,30 +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 androidx.test.ext.junit.runners;
-
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.InitializationError;
-
-// TODO: We need to simulate the androidx test runner.
-// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/ext/junit/java/androidx/test/ext/junit/runners/AndroidJUnit4.java
-// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/runner/android_junit_runner/java/androidx/test/internal/runner/junit4/AndroidJUnit4ClassRunner.java
-
-public class AndroidJUnit4 extends BlockJUnit4ClassRunner {
- public AndroidJUnit4(Class<?> testClass) throws InitializationError {
- super(testClass);
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java
deleted file mode 100644
index 2470d839..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-package androidx.test.filters;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Designates a test as being flaky (non-deterministic).
- *
- * <p>Can then be used to filter tests on execution using -e annotation or -e notAnnotation as
- * desired.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface FlakyTest {
- /**
- * An optional bug number associated with the test. -1 Means that no bug number is associated with
- * the flaky annotation.
- *
- * @return int
- */
- int bugId() default -1;
-
- /**
- * Details, such as the reason of why the test is flaky.
- *
- * @return String
- */
- String detail() default "";
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java
deleted file mode 100644
index 578d7dc..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.test.filters;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation to assign a large test size qualifier to a test. This annotation can be used at a
- * method or class level.
- *
- * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a
- * test suite of similar run time.
- *
- * <p>Execution time: >1000ms
- *
- * <p>Large tests should be focused on testing integration of all application components. These
- * tests fully participate in the system and may make use of all resources such as databases, file
- * systems and network. As a rule of thumb most functional UI tests are large tests.
- *
- * <p>Note: This class replaces the deprecated Android platform size qualifier <a
- * href="{@docRoot}reference/android/test/suitebuilder/annotation/LargeTest.html"><code>
- * android.test.suitebuilder.annotation.LargeTest</code></a> and is the recommended way to annotate
- * tests written with the AndroidX Test Library.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface LargeTest {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java
deleted file mode 100644
index dfdaa53..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.test.filters;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation to assign a medium test size qualifier to a test. This annotation can be used at a
- * method or class level.
- *
- * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a
- * test suite of similar run time.
- *
- * <p>Execution time: <1000ms
- *
- * <p>Medium tests should be focused on a very limited subset of components or a single component.
- * Resource access to the file system through well defined interfaces like databases,
- * ContentProviders, or Context is permitted. Network access should be restricted, (long-running)
- * blocking operations should be avoided and use mock objects instead.
- *
- * <p>Note: This class replaces the deprecated Android platform size qualifier <a
- * href="{@docRoot}reference/android/test/suitebuilder/annotation/MediumTest.html"><code>
- * android.test.suitebuilder.annotation.MediumTest</code></a> and is the recommended way to annotate
- * tests written with the AndroidX Test Library.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface MediumTest {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java
deleted file mode 100644
index 3d3ee33..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-package androidx.test.filters;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Indicates that a specific test should not be run on emulator.
- *
- * <p>It will be executed only if the test is running on the physical android device.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.METHOD})
-public @interface RequiresDevice {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java
deleted file mode 100644
index dd65ddb..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-package androidx.test.filters;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Indicates that a specific test or class requires a minimum or maximum API Level to execute.
- *
- * <p>Test(s) will be skipped when executed on android platforms less/more than specified level
- * (inclusive).
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE, ElementType.METHOD})
-public @interface SdkSuppress {
- /** The minimum API level to execute (inclusive) */
- int minSdkVersion() default 1;
- /** The maximum API level to execute (inclusive) */
- int maxSdkVersion() default Integer.MAX_VALUE;
- /**
- * The {@link android.os.Build.VERSION.CODENAME} to execute on. This is intended to be used to run
- * on a pre-release SDK, where the {@link android.os.Build.VERSION.SDK_INT} has not yet been
- * finalized. This is treated as an OR operation with respect to the minSdkVersion and
- * maxSdkVersion attributes.
- *
- * <p>For example, to filter a test so it runs on only the prerelease R SDK: <code>
- * {@literal @}SdkSuppress(minSdkVersion = Build.VERSION_CODES.R, codeName = "R")
- * </code>
- */
- String codeName() default "unset";
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java
deleted file mode 100644
index dd32df4..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.test.filters;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation to assign a small test size qualifier to a test. This annotation can be used at a
- * method or class level.
- *
- * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a
- * test suite of similar run time.
- *
- * <p>Execution time: <200ms
- *
- * <p>Small tests should be run very frequently. Focused on units of code to verify specific logical
- * conditions. These tests should runs in an isolated environment and use mock objects for external
- * dependencies. Resource access (such as file system, network, or databases) are not permitted.
- * Tests that interact with hardware, make binder calls, or that facilitate android instrumentation
- * should not use this annotation.
- *
- * <p>Note: This class replaces the deprecated Android platform size qualifier <a
- * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/SmallTest.html">
- * android.test.suitebuilder.annotation.SmallTest</a> and is the recommended way to annotate tests
- * written with the AndroidX Test Library.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface SmallTest {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java
deleted file mode 100644
index 88e636c..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.test.filters;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Use this annotation on test classes or test methods that should not be included in a test suite.
- * If the annotation appears on the class then no tests in that class will be included. If the
- * annotation appears only on a test method then only that method will be excluded.
- *
- * <p>Note: This class replaces the deprecated Android platform annotation <a
- * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/Suppress.html">
- * android.test.suitebuilder.annotation.Suppress</a> and is the recommended way to suppress tests
- * written with the AndroidX Test Library.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface Suppress {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java
deleted file mode 100644
index e137939..0000000
--- a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java
+++ /dev/null
@@ -1,24 +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 androidx.test.runner;
-
-import org.junit.runners.model.InitializationError;
-
-public class AndroidJUnit4 extends androidx.test.ext.junit.runners.AndroidJUnit4 {
- public AndroidJUnit4(Class<?> testClass) throws InitializationError {
- super(testClass);
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-framework/Android.bp
deleted file mode 100644
index 2b91cc1..0000000
--- a/tools/hoststubgen/hoststubgen/test-framework/Android.bp
+++ /dev/null
@@ -1,19 +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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-build = ["AndroidHostTest.bp"]
diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
deleted file mode 100644
index 1f8382a..0000000
--- a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
+++ /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.
-
-// Add `build = ["AndroidHostTest.bp"]` to Android.bp to include this file.
-
-// Compile the test jar, using 2 rules.
-// 1. Build the test against the stub.
-java_library_host {
- name: "HostStubGenTest-framework-test-host-test-lib",
- defaults: ["hosttest-with-framework-all-hidden-api-test-lib-defaults"],
- srcs: [
- "src/**/*.java",
- ],
- static_libs: [
- "junit",
- "truth",
- "mockito",
-
- // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/
- "platform-test-annotations",
- "hoststubgen-annotations",
- ],
-}
-
-// 2. Link the above module with necessary runtime dependencies, so it can be executed stand-alone.
-java_test_host {
- name: "HostStubGenTest-framework-all-test-host-test",
- defaults: ["hosttest-with-framework-all-hidden-api-test-defaults"],
- static_libs: [
- "HostStubGenTest-framework-test-host-test-lib",
- ],
- test_suites: ["general-tests"],
-}
-
-// "Productionized" build rule.
-android_ravenwood_test {
- name: "HostStubGenTest-framework-test",
- srcs: [
- "src/**/*.java",
- ],
- static_libs: [
- "junit",
- "truth",
- "mockito",
- ],
-}
diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml b/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml
deleted file mode 100644
index f35dcf6..0000000
--- a/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- [Ravenwood] Copied from $ANDROID_BUILD_TOP/cts/hostsidetests/devicepolicy/AndroidTest.xml -->
-<configuration description="CtsContentTestCases host-side test">
- <option name="test-suite-tag" value="ravenwood" />
- <option name="config-descriptor:metadata" key="component" value="framework" />
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
- <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
-
- <test class="com.android.tradefed.testtype.IsolatedHostTest" >
- <option name="jar" value="HostStubGenTest-framework-all-test-host-test.jar" />
- </test>
-</configuration>
diff --git a/tools/hoststubgen/hoststubgen/test-framework/README.md b/tools/hoststubgen/hoststubgen/test-framework/README.md
deleted file mode 100644
index 26a9ad1..0000000
--- a/tools/hoststubgen/hoststubgen/test-framework/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# HostStubGen: (obsolete) real framework test
-
-This directory contains tests against the actual framework.jar code. The tests were
-copied from somewhere else in the android tree. We use this directory to quickly run existing
-tests.
-
-This directory was used during the prototype phase, but now that we have real ravenwood tests,
-this directory is obsolete and should be deleted.
-
-## How to run
-
-- With `atest`. This is the proper way to run it, but it may fail due to atest's known problems.
-
-```
-$ atest HostStubGenTest-framework-all-test-host-test
-```
-
-- Advanced option: `run-test-without-atest.sh` runs the test without using `atest`
-
-```
-$ ./run-test-without-atest.sh
-```
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh b/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh
deleted file mode 100755
index cfc06a1..0000000
--- a/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/bash
-# 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.
-
-# Run HostStubGenTest-framework-test-host-test directly with JUnit.
-# (without using atest.)
-
-source "${0%/*}"/../../common.sh
-
-
-# Options:
-# -v enable verbose log
-# -d enable debugger
-
-verbose=0
-debug=0
-while getopts "vd" opt; do
- case "$opt" in
- v) verbose=1 ;;
- d) debug=1 ;;
- esac
-done
-shift $(($OPTIND - 1))
-
-
-if (( $verbose )) ; then
- JAVA_OPTS="$JAVA_OPTS -verbose:class"
-fi
-
-if (( $debug )) ; then
- JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8700"
-fi
-
-#=======================================
-module=HostStubGenTest-framework-all-test-host-test
-module_jar=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/$module/$module.jar
-run m $module
-
-out=out
-
-rm -fr $out
-mkdir -p $out
-
-
-# Copy and extract the relevant jar files so we can look into them.
-run cp \
- $module_jar \
- $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/*.jar \
- $out
-
-run extract $out/*.jar
-
-# Result is the number of failed tests.
-result=0
-
-
-# This suite runs all tests in the JAR.
-tests=(com.android.hoststubgen.hosthelper.HostTestSuite)
-
-# Uncomment this to run a specific test.
-# tests=(com.android.hoststubgen.frameworktest.LogTest)
-
-
-for class in ${tests[@]} ; do
- echo "Running $class ..."
-
- run cd "${module_jar%/*}"
- run $JAVA $JAVA_OPTS \
- -cp $module_jar \
- org.junit.runner.JUnitCore \
- $class || result=$(( $result + 1 ))
-done
-
-exit $result
diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java
deleted file mode 100644
index 2c5949c..0000000
--- a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java
+++ /dev/null
@@ -1,472 +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.frameworktest;
-
-// [ravewnwood] Copied from cts/, and commented out unsupported stuff.
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.util.ArrayMap;
-import android.util.Log;
-
-import org.junit.Test;
-
-import java.util.AbstractMap;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.function.BiFunction;
-
-/**
- * Some basic tests for {@link android.util.ArrayMap}.
- */
-public class ArrayMapTest {
- static final boolean DEBUG = false;
-
- private static boolean compare(Object v1, Object v2) {
- if (v1 == null) {
- return v2 == null;
- }
- if (v2 == null) {
- return false;
- }
- return v1.equals(v2);
- }
-
- private static void compareMaps(HashMap map, ArrayMap array) {
- if (map.size() != array.size()) {
- fail("Bad size: expected " + map.size() + ", got " + array.size());
- }
-
- Set<Entry> mapSet = map.entrySet();
- for (Map.Entry entry : mapSet) {
- Object expValue = entry.getValue();
- Object gotValue = array.get(entry.getKey());
- if (!compare(expValue, gotValue)) {
- fail("Bad value: expected " + expValue + ", got " + gotValue
- + " at key " + entry.getKey());
- }
- }
-
- for (int i = 0; i < array.size(); i++) {
- Object gotValue = array.valueAt(i);
- Object key = array.keyAt(i);
- Object expValue = map.get(key);
- if (!compare(expValue, gotValue)) {
- fail("Bad value: expected " + expValue + ", got " + gotValue
- + " at key " + key);
- }
- }
-
- if (map.entrySet().hashCode() != array.entrySet().hashCode()) {
- fail("Entry set hash codes differ: map=0x"
- + Integer.toHexString(map.entrySet().hashCode()) + " array=0x"
- + Integer.toHexString(array.entrySet().hashCode()));
- }
-
- if (!map.entrySet().equals(array.entrySet())) {
- fail("Failed calling equals on map entry set against array set");
- }
-
- if (!array.entrySet().equals(map.entrySet())) {
- fail("Failed calling equals on array entry set against map set");
- }
-
- if (map.keySet().hashCode() != array.keySet().hashCode()) {
- fail("Key set hash codes differ: map=0x"
- + Integer.toHexString(map.keySet().hashCode()) + " array=0x"
- + Integer.toHexString(array.keySet().hashCode()));
- }
-
- if (!map.keySet().equals(array.keySet())) {
- fail("Failed calling equals on map key set against array set");
- }
-
- if (!array.keySet().equals(map.keySet())) {
- fail("Failed calling equals on array key set against map set");
- }
-
- if (!map.keySet().containsAll(array.keySet())) {
- fail("Failed map key set contains all of array key set");
- }
-
- if (!array.keySet().containsAll(map.keySet())) {
- fail("Failed array key set contains all of map key set");
- }
-
- if (!array.containsAll(map.keySet())) {
- fail("Failed array contains all of map key set");
- }
-
- if (!map.entrySet().containsAll(array.entrySet())) {
- fail("Failed map entry set contains all of array entry set");
- }
-
- if (!array.entrySet().containsAll(map.entrySet())) {
- fail("Failed array entry set contains all of map entry set");
- }
- }
-
- private static void validateArrayMap(ArrayMap array) {
- Set<Map.Entry> entrySet = array.entrySet();
- int index = 0;
- Iterator<Entry> entryIt = entrySet.iterator();
- while (entryIt.hasNext()) {
- Map.Entry entry = entryIt.next();
- Object value = entry.getKey();
- Object realValue = array.keyAt(index);
- if (!compare(realValue, value)) {
- fail("Bad array map entry set: expected key " + realValue
- + ", got " + value + " at index " + index);
- }
- value = entry.getValue();
- realValue = array.valueAt(index);
- if (!compare(realValue, value)) {
- fail("Bad array map entry set: expected value " + realValue
- + ", got " + value + " at index " + index);
- }
- index++;
- }
-
- index = 0;
- Set keySet = array.keySet();
- Iterator keyIt = keySet.iterator();
- while (keyIt.hasNext()) {
- Object value = keyIt.next();
- Object realValue = array.keyAt(index);
- if (!compare(realValue, value)) {
- fail("Bad array map key set: expected key " + realValue
- + ", got " + value + " at index " + index);
- }
- index++;
- }
-
- index = 0;
- Collection valueCol = array.values();
- Iterator valueIt = valueCol.iterator();
- while (valueIt.hasNext()) {
- Object value = valueIt.next();
- Object realValue = array.valueAt(index);
- if (!compare(realValue, value)) {
- fail("Bad array map value col: expected value " + realValue
- + ", got " + value + " at index " + index);
- }
- index++;
- }
- }
-
- private static void dump(Map map, ArrayMap array) {
- Log.e("test", "HashMap of " + map.size() + " entries:");
- Set<Map.Entry> mapSet = map.entrySet();
- for (Map.Entry entry : mapSet) {
- Log.e("test", " " + entry.getKey() + " -> " + entry.getValue());
- }
- Log.e("test", "ArrayMap of " + array.size() + " entries:");
- for (int i = 0; i < array.size(); i++) {
- Log.e("test", " " + array.keyAt(i) + " -> " + array.valueAt(i));
- }
- }
-
- private static void dump(ArrayMap map1, ArrayMap map2) {
- Log.e("test", "ArrayMap of " + map1.size() + " entries:");
- for (int i = 0; i < map1.size(); i++) {
- Log.e("test", " " + map1.keyAt(i) + " -> " + map1.valueAt(i));
- }
- Log.e("test", "ArrayMap of " + map2.size() + " entries:");
- for (int i = 0; i < map2.size(); i++) {
- Log.e("test", " " + map2.keyAt(i) + " -> " + map2.valueAt(i));
- }
- }
-
- @Test
- public void testCopyArrayMap() {
- // map copy constructor test
- ArrayMap newMap = new ArrayMap<Integer, String>();
- for (int i = 0; i < 10; ++i) {
- newMap.put(i, String.valueOf(i));
- }
- ArrayMap mapCopy = new ArrayMap(newMap);
- if (!compare(mapCopy, newMap)) {
- String msg = "ArrayMap copy constructor failure: expected " +
- newMap + ", got " + mapCopy;
- Log.e("test", msg);
- dump(newMap, mapCopy);
- fail(msg);
- return;
- }
- }
-
- @Test
- public void testEqualsArrayMap() {
- ArrayMap<Integer, String> map1 = new ArrayMap<>();
- ArrayMap<Integer, String> map2 = new ArrayMap<>();
- HashMap<Integer, String> map3 = new HashMap<>();
- if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
- fail("ArrayMap equals failure for empty maps " + map1 + ", " +
- map2 + ", " + map3);
- }
-
- for (int i = 0; i < 10; ++i) {
- String value = String.valueOf(i);
- map1.put(i, value);
- map2.put(i, value);
- map3.put(i, value);
- }
- if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
- fail("ArrayMap equals failure for populated maps " + map1 + ", " +
- map2 + ", " + map3);
- }
-
- map1.remove(0);
- if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
- fail("ArrayMap equals failure for map size " + map1 + ", " +
- map2 + ", " + map3);
- }
-
- map1.put(0, "-1");
- if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
- fail("ArrayMap equals failure for map contents " + map1 + ", " +
- map2 + ", " + map3);
- }
- }
-
- private static void checkEntrySetToArray(ArrayMap<?, ?> testMap) {
- try {
- testMap.entrySet().toArray();
- fail();
- } catch (UnsupportedOperationException expected) {
- }
-
- try {
- Map.Entry<?, ?>[] entries = new Map.Entry[20];
- testMap.entrySet().toArray(entries);
- fail();
- } catch (UnsupportedOperationException expected) {
- }
- }
-
- // http://b/32294038, Test ArrayMap.entrySet().toArray()
- @Test
- public void testEntrySetArray() {
- // Create
- ArrayMap<Integer, String> testMap = new ArrayMap<>();
-
- // Test empty
- checkEntrySetToArray(testMap);
-
- // Test non-empty
- for (int i = 0; i < 10; ++i) {
- testMap.put(i, String.valueOf(i));
- }
- checkEntrySetToArray(testMap);
- }
-
- @Test
- public void testCanNotIteratePastEnd_entrySetIterator() {
- Map<String, String> map = new ArrayMap<>();
- map.put("key 1", "value 1");
- map.put("key 2", "value 2");
- Set<Map.Entry<String, String>> expectedEntriesToIterate = new HashSet<>(Arrays.asList(
- entryOf("key 1", "value 1"),
- entryOf("key 2", "value 2")
- ));
- Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
-
- // Assert iteration over the expected two entries in any order
- assertTrue(iterator.hasNext());
- Map.Entry<String, String> firstEntry = copyOf(iterator.next());
- assertTrue(expectedEntriesToIterate.remove(firstEntry));
-
- assertTrue(iterator.hasNext());
- Map.Entry<String, String> secondEntry = copyOf(iterator.next());
- assertTrue(expectedEntriesToIterate.remove(secondEntry));
-
- assertFalse(iterator.hasNext());
-
- try {
- iterator.next();
- fail();
- } catch (NoSuchElementException expected) {
- }
- }
-
- private static <K, V> Map.Entry<K, V> entryOf(K key, V value) {
- return new AbstractMap.SimpleEntry<>(key, value);
- }
-
- private static <K, V> Map.Entry<K, V> copyOf(Map.Entry<K, V> entry) {
- return entryOf(entry.getKey(), entry.getValue());
- }
-
- @Test
- public void testCanNotIteratePastEnd_keySetIterator() {
- Map<String, String> map = new ArrayMap<>();
- map.put("key 1", "value 1");
- map.put("key 2", "value 2");
- Set<String> expectedKeysToIterate = new HashSet<>(Arrays.asList("key 1", "key 2"));
- Iterator<String> iterator = map.keySet().iterator();
-
- // Assert iteration over the expected two keys in any order
- assertTrue(iterator.hasNext());
- String firstKey = iterator.next();
- assertTrue(expectedKeysToIterate.remove(firstKey));
-
- assertTrue(iterator.hasNext());
- String secondKey = iterator.next();
- assertTrue(expectedKeysToIterate.remove(secondKey));
-
- assertFalse(iterator.hasNext());
-
- try {
- iterator.next();
- fail();
- } catch (NoSuchElementException expected) {
- }
- }
-
- @Test
- public void testCanNotIteratePastEnd_valuesIterator() {
- Map<String, String> map = new ArrayMap<>();
- map.put("key 1", "value 1");
- map.put("key 2", "value 2");
- Set<String> expectedValuesToIterate = new HashSet<>(Arrays.asList("value 1", "value 2"));
- Iterator<String> iterator = map.values().iterator();
-
- // Assert iteration over the expected two values in any order
- assertTrue(iterator.hasNext());
- String firstValue = iterator.next();
- assertTrue(expectedValuesToIterate.remove(firstValue));
-
- assertTrue(iterator.hasNext());
- String secondValue = iterator.next();
- assertTrue(expectedValuesToIterate.remove(secondValue));
-
- assertFalse(iterator.hasNext());
-
- try {
- iterator.next();
- fail();
- } catch (NoSuchElementException expected) {
- }
- }
-
- @Test
- public void testForEach() {
- ArrayMap<String, Integer> map = new ArrayMap<>();
-
- for (int i = 0; i < 50; ++i) {
- map.put(Integer.toString(i), i * 10);
- }
-
- // Make sure forEach goes through all of the elements.
- HashMap<String, Integer> seen = new HashMap<>();
- map.forEach(seen::put);
- compareMaps(seen, map);
- }
-
- /**
- * The entrySet Iterator returns itself from each call to {@code next()}. This is unusual
- * behavior for {@link Iterator#next()}; this test ensures that any future change to this
- * behavior is deliberate.
- */
- @Test
- public void testUnusualBehavior_eachEntryIsSameAsIterator_entrySetIterator() {
- Map<String, String> map = new ArrayMap<>();
- map.put("key 1", "value 1");
- map.put("key 2", "value 2");
- Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
-
- assertSame(iterator, iterator.next());
- assertSame(iterator, iterator.next());
- }
-
- @SuppressWarnings("SelfEquals")
- @Test
- public void testUnusualBehavior_equalsThrowsAfterRemove_entrySetIterator() {
- Map<String, String> map = new ArrayMap<>();
- map.put("key 1", "value 1");
- map.put("key 2", "value 2");
- Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
- iterator.next();
- iterator.remove();
- try {
- iterator.equals(iterator);
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- private static <T> void assertEqualsBothWays(T a, T b) {
- assertEquals(a, b);
- assertEquals(b, a);
- assertEquals(a.hashCode(), b.hashCode());
- }
-
- @Test
- public void testRemoveAll() {
- final ArrayMap<Integer, String> map = new ArrayMap<>();
- for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) {
- map.put(i, i.toString());
- }
-
- final ArrayMap<Integer, String> expectedMap = new ArrayMap<>();
- for (Integer i : Arrays.asList(2, 4)) {
- expectedMap.put(i, String.valueOf(i));
- }
- map.removeAll(Arrays.asList(0, 1, 3, 5, 6));
- if (!compare(map, expectedMap)) {
- fail("ArrayMap removeAll failure, expect " + expectedMap + ", but " + map);
- }
-
- map.removeAll(Collections.emptyList());
- if (!compare(map, expectedMap)) {
- fail("ArrayMap removeAll failure for empty maps, expect " + expectedMap + ", but " +
- map);
- }
-
- map.removeAll(Arrays.asList(2, 4));
- if (!map.isEmpty()) {
- fail("ArrayMap removeAll failure, expect empty, but " + map);
- }
- }
-
- @Test
- public void testReplaceAll() {
- final ArrayMap<Integer, Integer> map = new ArrayMap<>();
- final ArrayMap<Integer, Integer> expectedMap = new ArrayMap<>();
- final BiFunction<Integer, Integer, Integer> function = (k, v) -> 2 * v;
- for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) {
- map.put(i, i);
- expectedMap.put(i, 2 * i);
- }
-
- map.replaceAll(function);
- if (!compare(map, expectedMap)) {
- fail("ArrayMap replaceAll failure, expect " + expectedMap + ", but " + map);
- }
- }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java
deleted file mode 100644
index 3e33b54..0000000
--- a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java
+++ /dev/null
@@ -1,41 +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.frameworktest;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.util.Log;
-
-import org.junit.Test;
-
-/**
- * Some basic tests for {@link android.util.Log}.
- */
-public class LogTest {
- @Test
- public void testBasicLogging() {
- Log.v("TAG", "Test v log");
- Log.d("TAG", "Test d log");
- Log.i("TAG", "Test i log");
- Log.w("TAG", "Test w log");
- Log.e("TAG", "Test e log");
- }
-
- @Test
- public void testNativeMethods() {
- assertThat(Log.isLoggable("mytag", Log.INFO)).isTrue();
- }
-}
diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh b/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh
deleted file mode 100755
index 7268123..0000000
--- a/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-# 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.
-
-
-# Script to build `framework-host-stub` and `framework-host-impl`, and copy the
-# generated jars to $out, and unzip them. Useful for looking into the generated files.
-
-source "${0%/*}"/../common.sh
-
-out=framework-all-stub-out
-
-rm -fr $out
-mkdir -p $out
-
-# Build the jars with `m`.
-run m framework-all-hidden-api-host
-
-# Copy the jar to out/ and extract them.
-run cp \
- $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/* \
- $out
-
-extract $out/*.jar
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 222c874..a6847ae 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -22,7 +22,6 @@
# These tests are known to pass.
READY_TEST_MODULES=(
- HostStubGenTest-framework-all-test-host-test
hoststubgen-test-tiny-test
CtsUtilTestCasesRavenwood
CtsOsTestCasesRavenwood # This one uses native sustitution, so let's run it too.
@@ -30,7 +29,6 @@
MUST_BUILD_MODULES=(
"${NOT_READY_TEST_MODULES[*]}"
- HostStubGenTest-framework-test
)
# First, build all the test / etc modules. This shouldn't fail.
@@ -44,11 +42,8 @@
# files, and they may fail when something changes in the build system.
run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh
-run ./hoststubgen/test-framework/run-test-without-atest.sh
-
run ./hoststubgen/test-tiny-framework/run-test-manually.sh
run atest $ATEST_ARGS tiny-framework-dump-test
-run ./scripts/build-framework-hostside-jars-and-extract.sh
# This script is already broken on goog/master
# run ./scripts/build-framework-hostside-jars-without-genrules.sh
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
index 25d208d..5cbb1aa 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -77,7 +77,9 @@
.autoFix()
.build()
- return LintFix.create().composite(annotateFix, *replaceOrRemoveFixes.toTypedArray())
+ return LintFix.create()
+ .name(annotateFix.getDisplayName())
+ .composite(annotateFix, *replaceOrRemoveFixes.toTypedArray())
}
private val annotation: String