diff options
1002 files changed, 19985 insertions, 5528 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index beb11fc3ee35..98b62b3e2046 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -26,6 +26,7 @@ aconfig_srcjars = [ ":android.content.flags-aconfig-java{.generated_srcjars}", ":android.content.pm.flags-aconfig-java{.generated_srcjars}", ":android.content.res.flags-aconfig-java{.generated_srcjars}", + ":android.crashrecovery.flags-aconfig-java{.generated_srcjars}", ":android.credentials.flags-aconfig-java{.generated_srcjars}", ":android.database.sqlite-aconfig-java{.generated_srcjars}", ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}", @@ -94,6 +95,7 @@ stubs_defaults { "android.companion.virtual.flags-aconfig", "android.content.pm.flags-aconfig", "android.content.res.flags-aconfig", + "android.crashrecovery.flags-aconfig", "android.credentials.flags-aconfig", "android.database.sqlite-aconfig", "android.hardware.biometrics.flags-aconfig", @@ -1078,3 +1080,16 @@ java_aconfig_library { aconfig_declarations: "android.adaptiveauth.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// CrashRecovery Module +aconfig_declarations { + name: "android.crashrecovery.flags-aconfig", + package: "android.crashrecovery.flags", + srcs: ["packages/CrashRecovery/aconfig/flags.aconfig"], +} + +java_aconfig_library { + name: "android.crashrecovery.flags-aconfig-java", + aconfig_declarations: "android.crashrecovery.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +}
\ No newline at end of file diff --git a/Android.bp b/Android.bp index 9c56733650cb..e12f74fcd7ca 100644 --- a/Android.bp +++ b/Android.bp @@ -95,6 +95,7 @@ filegroup { ":platform-compat-native-aidl", // AIDL sources from external directories + ":android.frameworks.location.altitude-V2-java-source", ":android.hardware.biometrics.common-V4-java-source", ":android.hardware.biometrics.fingerprint-V3-java-source", ":android.hardware.biometrics.face-V4-java-source", diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java index 0ea2dafbb047..bc8470888a5a 100644 --- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java +++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java @@ -248,6 +248,11 @@ public abstract class AbstractContentCapturePerfTestCase { return mServiceWatcher.waitOnCreate(); } + /** Wait for session paused. */ + public void waitForSessionPaused() throws InterruptedException { + mServiceWatcher.waitSessionPaused(); + } + @NonNull protected ActivityWatcher startWatcher() { return mActivitiesWatcher.watch(CustomTestActivity.class); diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java index aa95dfdfdf16..44e8a67e72ef 100644 --- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java +++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java @@ -80,6 +80,34 @@ public class LoginTest extends AbstractContentCapturePerfTestCase { testActivityLaunchTime(R.layout.test_container_activity, 500); } + @Test + public void testSendEventsLatency() throws Throwable { + enableService(); + + testSendEventLatency(R.layout.test_container_activity, 0); + } + + @Test + public void testSendEventsLatency_contains100Views() throws Throwable { + enableService(); + + testSendEventLatency(R.layout.test_container_activity, 100); + } + + @Test + public void testSendEventsLatency_contains300Views() throws Throwable { + enableService(); + + testSendEventLatency(R.layout.test_container_activity, 300); + } + + @Test + public void testSendEventsLatency_contains500Views() throws Throwable { + enableService(); + + testSendEventLatency(R.layout.test_container_activity, 500); + } + private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable { final Object drawNotifier = new Object(); final Intent intent = getLaunchIntent(layoutId, numViews); @@ -111,6 +139,38 @@ public class LoginTest extends AbstractContentCapturePerfTestCase { } } + private void testSendEventLatency(int layoutId, int numViews) throws Throwable { + final Object drawNotifier = new Object(); + final Intent intent = getLaunchIntent(layoutId, numViews); + intent.putExtra(CustomTestActivity.INTENT_EXTRA_FINISH_ON_IDLE, true); + intent.putExtra(CustomTestActivity.INTENT_EXTRA_DRAW_CALLBACK, + new RemoteCallback(result -> { + synchronized (drawNotifier) { + drawNotifier.notifyAll(); + } + })); + final ActivityWatcher watcher = startWatcher(); + + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mEntryActivity.startActivity(intent); + synchronized (drawNotifier) { + try { + drawNotifier.wait(GENERIC_TIMEOUT_MS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + waitForSessionPaused(); + + // Ignore the time to finish the activity + state.pauseTiming(); + watcher.waitFor(DESTROYED); + sInstrumentation.waitForIdleSync(); + state.resumeTiming(); + } + } + @Test public void testOnVisibilityAggregated_visibleChanged() throws Throwable { enableService(); diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java index ecc5112ab6dd..0b5345f5e2e1 100644 --- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java +++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java @@ -114,6 +114,10 @@ public class MyContentCaptureService extends ContentCaptureService { public void onContentCaptureEvent(ContentCaptureSessionId sessionId, ContentCaptureEvent event) { Log.i(TAG, "onContentCaptureEventsRequest(session=" + sessionId + "): " + event); + if (sServiceWatcher != null + && event.getType() == ContentCaptureEvent.TYPE_SESSION_PAUSED) { + sServiceWatcher.mSessionPaused.countDown(); + } } @Override @@ -126,6 +130,7 @@ public class MyContentCaptureService extends ContentCaptureService { private static final long GENERIC_TIMEOUT_MS = 10_000; private final CountDownLatch mCreated = new CountDownLatch(1); private final CountDownLatch mDestroyed = new CountDownLatch(1); + private final CountDownLatch mSessionPaused = new CountDownLatch(1); private boolean mReadyToClear = true; private Pair<Set<String>, Set<ComponentName>> mAllowList; @@ -151,6 +156,11 @@ public class MyContentCaptureService extends ContentCaptureService { await(mDestroyed, "not destroyed"); } + /** Wait for session paused. */ + public void waitSessionPaused() throws InterruptedException { + await(mSessionPaused, "no Paused"); + } + /** * Allow just this package. */ diff --git a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt index aadbc2319a62..add0a086201b 100644 --- a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt +++ b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt @@ -16,8 +16,6 @@ package android.input -import android.content.Context -import android.content.res.Resources import android.os.SystemProperties import android.perftests.utils.PerfStatusReporter import android.view.InputDevice @@ -38,8 +36,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` import java.time.Duration @@ -68,18 +64,6 @@ private fun getStylusMotionEvent( InputDevice.SOURCE_STYLUS, /*flags=*/0) } -private fun getPredictionContext(offset: Duration, enablePrediction: Boolean): Context { - val context = mock(Context::class.java) - val resources: Resources = mock(Resources::class.java) - `when`(context.getResources()).thenReturn(resources) - `when`(resources.getInteger( - com.android.internal.R.integer.config_motionPredictionOffsetNanos)).thenReturn( - offset.toNanos().toInt()) - `when`(resources.getBoolean( - com.android.internal.R.bool.config_enableMotionPrediction)).thenReturn(enablePrediction) - return context -} - @RunWith(AndroidJUnit4::class) @LargeTest class MotionPredictorBenchmark { @@ -115,7 +99,7 @@ class MotionPredictorBenchmark { var eventPosition = 0f val positionInterval = 10f - val predictor = MotionPredictor(getPredictionContext(offset, /*enablePrediction=*/true)) + val predictor = MotionPredictor(/*isPredictionEnabled=*/true, offset.toNanos().toInt()) // ACTION_DOWN t=0 x=0 y=0 predictor.record(getStylusMotionEvent( eventTime, ACTION_DOWN, /*x=*/eventPosition, /*y=*/eventPosition)) @@ -141,12 +125,11 @@ class MotionPredictorBenchmark { */ @Test fun timeCreatePredictor() { - val context = getPredictionContext( - /*offset=*/Duration.ofMillis(20), /*enablePrediction=*/true) + val offsetNanos = Duration.ofMillis(20).toNanos().toInt() val state = perfStatusReporter.getBenchmarkState() while (state.keepRunning()) { - MotionPredictor(context) + MotionPredictor(/*isPredictionEnabled=*/true, offsetNanos) } } } diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java index 6c8af39015f5..ae98fe14fbe6 100644 --- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java @@ -77,6 +77,12 @@ public interface JobSchedulerInternal { @NonNull String notificationChannel, int userId, @NonNull String packageName); /** + * @return {@code true} if the given package holds the + * {@link android.Manifest.permission.RUN_BACKUP_JOBS} permission. + */ + boolean hasRunBackupJobsPermission(@NonNull String packageName, int packageUid); + + /** * Report a snapshot of sync-related jobs back to the sync manager */ JobStorePersistStats getPersistStats(); 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 fc193d8147b5..57467e3cc83d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -4197,6 +4197,11 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override + public boolean hasRunBackupJobsPermission(@NonNull String packageName, int packageUid) { + return JobSchedulerService.this.hasRunBackupJobsPermission(packageName, packageUid); + } + + @Override public JobStorePersistStats getPersistStats() { synchronized (mLock) { return new JobStorePersistStats(mJobs.getPersistStats()); @@ -4359,6 +4364,22 @@ public class JobSchedulerService extends com.android.server.SystemService } /** + * Returns whether the app holds the {@link Manifest.permission.RUN_BACKUP_JOBS} permission. + */ + private boolean hasRunBackupJobsPermission(@NonNull String packageName, int packageUid) { + if (packageName == null) { + Slog.wtfStack(TAG, + "Expected a non-null package name when calling hasRunBackupJobsPermission"); + return false; + } + + return PermissionChecker.checkPermissionForPreflight(getTestableContext(), + android.Manifest.permission.RUN_BACKUP_JOBS, + PermissionChecker.PID_UNKNOWN, packageUid, packageName) + == PermissionChecker.PERMISSION_GRANTED; + } + + /** * Binder stub trampoline implementation */ final class JobSchedulerStub extends IJobScheduler.Stub { 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 a4df5d829281..2ea980d40287 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 @@ -1222,21 +1222,25 @@ public final class JobStatus { return ACTIVE_INDEX; } - final int bucketWithMediaExemption; - if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX - && mHasMediaBackupExemption) { + final boolean isEligibleAsBackupJob = job.getTriggerContentUris() != null + && job.getRequiredNetwork() != null + && !job.hasLateConstraint() + && mJobSchedulerInternal.hasRunBackupJobsPermission(sourcePackageName, sourceUid); + final boolean isBackupExempt = mHasMediaBackupExemption || isEligibleAsBackupJob; + final int bucketWithBackupExemption; + if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX && isBackupExempt) { // Treat it as if it's at most WORKING_INDEX (lower index grants higher quota) since // media backup jobs are important to the user, and the source package may not have // been used directly in a while. - bucketWithMediaExemption = Math.min(WORKING_INDEX, actualBucket); + bucketWithBackupExemption = Math.min(WORKING_INDEX, actualBucket); } else { - bucketWithMediaExemption = actualBucket; + bucketWithBackupExemption = actualBucket; } // If the app is considered buggy, but hasn't yet been put in the RESTRICTED bucket // (potentially because it's used frequently by the user), limit its effective bucket // so that it doesn't get to run as much as a normal ACTIVE app. - if (isBuggy && bucketWithMediaExemption < WORKING_INDEX) { + if (isBuggy && bucketWithBackupExemption < WORKING_INDEX) { if (!mIsDowngradedDueToBuggyApp) { // Safety check to avoid logging multiple times for the same job. Counter.logIncrementWithUid( @@ -1246,7 +1250,7 @@ public final class JobStatus { } return WORKING_INDEX; } - return bucketWithMediaExemption; + return bucketWithBackupExemption; } /** Returns the real standby bucket of the job. */ diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING index fd571c95f568..f1e4d0ee4906 100644 --- a/core/TEST_MAPPING +++ b/core/TEST_MAPPING @@ -20,5 +20,15 @@ "core/tests/coretests/src/com/android/internal/inputmethod/.*" ] } - ] + ], + "postsubmit": [ + { + "name": "CtsContactKeysManagerTestCases", + "options": [ + { + "include-filter": "android.provider.cts.contactkeys." + } + ] + } + ] } diff --git a/core/api/current.txt b/core/api/current.txt index c1026810fc12..920d6e00598d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -147,6 +147,7 @@ package android { field public static final String MANAGE_DEVICE_POLICY_CAMERA = "android.permission.MANAGE_DEVICE_POLICY_CAMERA"; field public static final String MANAGE_DEVICE_POLICY_CERTIFICATES = "android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES"; field public static final String MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE = "android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE"; + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String MANAGE_DEVICE_POLICY_CONTENT_PROTECTION = "android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"; field public static final String MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES = "android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"; field public static final String MANAGE_DEVICE_POLICY_DEFAULT_SMS = "android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS"; field public static final String MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS = "android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS"; @@ -444,6 +445,7 @@ package android { field public static final int alertDialogTheme = 16843529; // 0x1010309 field public static final int alignmentMode = 16843642; // 0x101037a field public static final int allContactsName = 16843468; // 0x10102cc + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int allow; field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601 field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 @@ -847,6 +849,7 @@ package android { field public static final int format24Hour = 16843723; // 0x10103cb field public static final int fraction = 16843992; // 0x10104d8 field public static final int fragment = 16843491; // 0x10102e3 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentAdvancedPattern; field public static final int fragmentAllowEnterTransitionOverlap = 16843976; // 0x10104c8 field public static final int fragmentAllowReturnTransitionOverlap = 16843977; // 0x10104c9 field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7 @@ -857,10 +860,13 @@ package android { field public static final int fragmentFadeExitAnimation = 16843498; // 0x10102ea field public static final int fragmentOpenEnterAnimation = 16843493; // 0x10102e5 field public static final int fragmentOpenExitAnimation = 16843494; // 0x10102e6 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPattern; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPrefix; field public static final int fragmentReenterTransition = 16843975; // 0x10104c7 field public static final int fragmentReturnTransition = 16843973; // 0x10104c5 field public static final int fragmentSharedElementEnterTransition = 16843972; // 0x10104c4 field public static final int fragmentSharedElementReturnTransition = 16843974; // 0x10104c6 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentSuffix; field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 @@ -1329,10 +1335,15 @@ package android { field public static final int propertyYName = 16843893; // 0x1010475 field public static final int protectionLevel = 16842761; // 0x1010009 field public static final int publicKey = 16843686; // 0x10103a6 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int query; field public static final int queryActionMsg = 16843227; // 0x10101db + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryAdvancedPattern; field public static final int queryAfterZeroResults = 16843394; // 0x1010282 field public static final int queryBackground = 16843911; // 0x1010487 field public static final int queryHint = 16843608; // 0x1010358 + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPattern; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPrefix; + field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int querySuffix; field public static final int quickContactBadgeStyleSmallWindowLarge = 16843443; // 0x10102b3 field public static final int quickContactBadgeStyleSmallWindowMedium = 16843442; // 0x10102b2 field public static final int quickContactBadgeStyleSmallWindowSmall = 16843441; // 0x10102b1 @@ -8184,6 +8195,9 @@ package android.app.admin { field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; field public static final String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION"; field public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED"; + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_DISABLED = 1; // 0x1 + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_ENABLED = 2; // 0x2 + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; // 0x0 field public static final String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions"; field public static final String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall"; field public static final String DELEGATION_CERT_INSTALL = "delegation-cert-install"; @@ -10594,6 +10608,7 @@ package android.content { field public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics"; field public static final String CONNECTIVITY_SERVICE = "connectivity"; field public static final String CONSUMER_IR_SERVICE = "consumer_ir"; + field @FlaggedApi("android.provider.user_keys") public static final String CONTACT_KEYS_SERVICE = "contact_keys"; field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2 field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1 field public static final int CONTEXT_RESTRICTED = 4; // 0x4 @@ -11387,10 +11402,12 @@ package android.content { method public final void addDataScheme(String); method public final void addDataSchemeSpecificPart(String, int); method public final void addDataType(String) throws android.content.IntentFilter.MalformedMimeTypeException; + method @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final void addUriRelativeFilterGroup(@NonNull android.content.UriRelativeFilterGroup); method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicate(); method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicateWithTypeResolution(@NonNull android.content.ContentResolver); method public final java.util.Iterator<android.content.IntentFilter.AuthorityEntry> authoritiesIterator(); method public final java.util.Iterator<java.lang.String> categoriesIterator(); + method @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final void clearUriRelativeFilterGroups(); method public final int countActions(); method public final int countCategories(); method public final int countDataAuthorities(); @@ -11398,6 +11415,7 @@ package android.content { method public final int countDataSchemeSpecificParts(); method public final int countDataSchemes(); method public final int countDataTypes(); + method @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final int countUriRelativeFilterGroups(); method public static android.content.IntentFilter create(String, String); method public final int describeContents(); method public void dump(android.util.Printer, String); @@ -11409,6 +11427,7 @@ package android.content { method public final android.os.PatternMatcher getDataSchemeSpecificPart(int); method public final String getDataType(int); method public final int getPriority(); + method @FlaggedApi("android.content.pm.relative_reference_intent_filters") @NonNull public final android.content.UriRelativeFilterGroup getUriRelativeFilterGroup(int); method public final boolean hasAction(String); method public final boolean hasCategory(String); method public final boolean hasDataAuthority(android.net.Uri); @@ -11830,6 +11849,27 @@ package android.content { field public static final long INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L } + @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final class UriRelativeFilter { + ctor public UriRelativeFilter(int, int, @NonNull String); + method @NonNull public String getFilter(); + method public int getPatternType(); + method public int getUriPart(); + method public boolean matchData(@NonNull android.net.Uri); + field public static final int FRAGMENT = 2; // 0x2 + field public static final int PATH = 0; // 0x0 + field public static final int QUERY = 1; // 0x1 + } + + @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final class UriRelativeFilterGroup { + ctor public UriRelativeFilterGroup(int); + method public void addUriRelativeFilter(@NonNull android.content.UriRelativeFilter); + method public int getAction(); + method @NonNull public java.util.Collection<android.content.UriRelativeFilter> getUriRelativeFilters(); + method public boolean matchData(@NonNull android.net.Uri); + field public static final int ACTION_ALLOW = 0; // 0x0 + field public static final int ACTION_BLOCK = 1; // 0x1 + } + } package android.content.om { @@ -12351,6 +12391,7 @@ package android.content.pm { method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler); method public void registerPackageInstallerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.content.pm.PackageInstaller.SessionCallback); method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle); + method @FlaggedApi("android.content.pm.archiving") public void setArchiveCompatibilityOptions(boolean, boolean); method public boolean shouldHideFromSuggestions(@NonNull String, @NonNull android.os.UserHandle); method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); @@ -22335,6 +22376,7 @@ package android.media { method @NonNull public java.util.List<java.lang.String> getSupportedVendorParameters(); method @Nullable public static android.media.Image mapHardwareBuffer(@NonNull android.hardware.HardwareBuffer); method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException; + method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public void queueInputBuffers(int, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>); method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException; method public void release(); method public void releaseOutputBuffer(int, boolean); @@ -22396,6 +22438,7 @@ package android.media { method public abstract void onError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CodecException); method public abstract void onInputBufferAvailable(@NonNull android.media.MediaCodec, int); method public abstract void onOutputBufferAvailable(@NonNull android.media.MediaCodec, int, @NonNull android.media.MediaCodec.BufferInfo); + method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public void onOutputBuffersAvailable(@NonNull android.media.MediaCodec, int, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>); method public abstract void onOutputFormatChanged(@NonNull android.media.MediaCodec, @NonNull android.media.MediaFormat); } @@ -22483,6 +22526,7 @@ package android.media { } public static final class MediaCodec.OutputFrame { + method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public java.util.ArrayDeque<android.media.MediaCodec.BufferInfo> getBufferInfos(); method @NonNull public java.util.Set<java.lang.String> getChangedKeys(); method public int getFlags(); method @NonNull public android.media.MediaFormat getFormat(); @@ -22498,6 +22542,7 @@ package android.media { public final class MediaCodec.QueueRequest { method public void queue(); + method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public android.media.MediaCodec.QueueRequest setBufferInfos(@NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>); method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @NonNull android.media.MediaCodec.CryptoInfo); method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int); @@ -35196,6 +35241,54 @@ package android.provider { field public static final String LONGITUDE = "longitude"; } + @FlaggedApi("android.provider.user_keys") public class ContactKeysManager { + method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.ContactKey> getAllContactKeys(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.SelfKey> getAllSelfKeys(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public android.provider.ContactKeysManager.ContactKey getContactKey(@NonNull String, @NonNull String, @NonNull String); + method public static int getMaxKeySizeBytes(); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.ContactKey> getOwnerContactKeys(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public java.util.List<android.provider.ContactKeysManager.SelfKey> getOwnerSelfKeys(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public android.provider.ContactKeysManager.SelfKey getSelfKey(@NonNull String, @NonNull String); + method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean removeContactKey(@NonNull String, @NonNull String, @NonNull String); + method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean removeSelfKey(@NonNull String, @NonNull String); + method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateContactKeyLocalVerificationState(@NonNull String, @NonNull String, @NonNull String, int); + method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateContactKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, int); + method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public void updateOrInsertContactKey(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]); + method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateOrInsertSelfKey(@NonNull String, @NonNull String, @NonNull byte[]); + method @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) public boolean updateSelfKeyRemoteVerificationState(@NonNull String, @NonNull String, int); + field public static final int UNVERIFIED = 0; // 0x0 + field public static final int VERIFICATION_FAILED = 1; // 0x1 + field public static final int VERIFIED = 2; // 0x2 + } + + public static final class ContactKeysManager.ContactKey implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getAccountId(); + method @Nullable public String getDeviceId(); + method @Nullable public String getDisplayName(); + method @Nullable public String getEmailAddress(); + method @Nullable public byte[] getKeyValue(); + method public int getLocalVerificationState(); + method @NonNull public String getOwnerPackageName(); + method @Nullable public String getPhoneNumber(); + method public int getRemoteVerificationState(); + method public long getTimeUpdated(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.provider.ContactKeysManager.ContactKey> CREATOR; + } + + public static final class ContactKeysManager.SelfKey implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getAccountId(); + method @Nullable public String getDeviceId(); + method @Nullable public byte[] getKeyValue(); + method @NonNull public String getOwnerPackageName(); + method public int getRemoteVerificationState(); + method public long getTimeUpdated(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.provider.ContactKeysManager.SelfKey> CREATOR; + } + @Deprecated public class Contacts { field @Deprecated public static final String AUTHORITY = "contacts"; field @Deprecated public static final android.net.Uri CONTENT_URI; @@ -53667,13 +53760,13 @@ package android.view { method @Deprecated public android.view.Display getDefaultDisplay(); method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics(); method public default boolean isCrossWindowBlurEnabled(); - method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver); method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer); method public void removeViewImmediate(android.view.View); - method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.os.IBinder); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.view.SurfaceControl); method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"; field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ea008ac2b5c0..f1011616c3ab 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -129,6 +129,8 @@ package android { field public static final String DISABLE_SYSTEM_SOUND_EFFECTS = "android.permission.DISABLE_SYSTEM_SOUND_EFFECTS"; field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE"; field public static final String DOMAIN_VERIFICATION_AGENT = "android.permission.DOMAIN_VERIFICATION_AGENT"; + field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE"; + field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES"; field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED"; field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"; field public static final String FORCE_BACK = "android.permission.FORCE_BACK"; @@ -559,7 +561,7 @@ package android.app { public class ActivityManager { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); - method @FlaggedApi("android.app.uid_importance_listener_for_uids") @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(@NonNull android.app.ActivityManager.OnUidImportanceListener, int, @Nullable int[]); + method @FlaggedApi("android.app.uid_importance_listener_for_uids") @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(@NonNull android.app.ActivityManager.OnUidImportanceListener, int, @NonNull int[]); method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); method @FlaggedApi("android.app.get_binding_uid_importance") @RequiresPermission(android.Manifest.permission.GET_BINDING_UID_IMPORTANCE) public int getBindingUidImportance(int); method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser(); @@ -4231,6 +4233,7 @@ package android.content.pm { public final class UserProperties implements android.os.Parcelable { method public int describeContents(); method public int getCrossProfileContentSharingStrategy(); + method @FlaggedApi("android.multiuser.support_hiding_profiles") @NonNull public int getProfileApiVisibility(); method public int getShowInQuietMode(); method public int getShowInSharingSurfaces(); method public boolean isCredentialShareableWithParent(); @@ -4240,6 +4243,9 @@ package android.content.pm { field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1 field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0 field public static final int CROSS_PROFILE_CONTENT_SHARING_UNKNOWN = -1; // 0xffffffff + field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_HIDDEN = 1; // 0x1 + field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_UNKNOWN = -1; // 0xffffffff + field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_VISIBLE = 0; // 0x0 field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2 field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1 field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0 @@ -4375,6 +4381,41 @@ package android.content.rollback { } +package android.credentials.selection { + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class FailureResult { + ctor public FailureResult(int, @Nullable String); + method public int getErrorCode(); + method @Nullable public String getErrorMessage(); + field public static final int ERROR_CODE_CANCELED_AND_LAUNCHED_SETTINGS = 2; // 0x2 + field public static final int ERROR_CODE_DIALOG_CANCELED_BY_USER = 1; // 0x1 + field public static final int ERROR_CODE_UI_FAILURE = 0; // 0x0 + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class ProviderPendingIntentResponse implements android.os.Parcelable { + ctor public ProviderPendingIntentResponse(int, @Nullable android.content.Intent); + method public int describeContents(); + method public int getResultCode(); + method @Nullable public android.content.Intent getResultData(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.ProviderPendingIntentResponse> CREATOR; + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class ResultHelper { + method public static void sendFailureResult(@NonNull android.os.ResultReceiver, @NonNull android.credentials.selection.FailureResult); + method public static void sendUserSelectionResult(@NonNull android.os.ResultReceiver, @NonNull android.credentials.selection.UserSelectionResult); + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class UserSelectionResult { + ctor public UserSelectionResult(@NonNull String, @NonNull String, @NonNull String, @Nullable android.credentials.selection.ProviderPendingIntentResponse); + method @NonNull public String getEntryKey(); + method @NonNull public String getEntrySubkey(); + method @Nullable public android.credentials.selection.ProviderPendingIntentResponse getPendingIntentProviderResponse(); + method @NonNull public String getProviderId(); + } + +} + package android.database { public abstract class ContentObserver { @@ -6124,6 +6165,7 @@ package android.hardware.radio { method public boolean containsKey(String); method public int describeContents(); method @Deprecated public android.graphics.Bitmap getBitmap(String); + method @FlaggedApi("android.hardware.radio.hd_radio_improved") public int getBitmapId(@NonNull String); method public android.hardware.radio.RadioMetadata.Clock getClock(String); method public int getInt(String); method public String getString(String); @@ -6187,6 +6229,7 @@ package android.hardware.radio { method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void close(); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public android.hardware.radio.ProgramList getDynamicProgramList(@Nullable android.hardware.radio.ProgramList.Filter); + method @FlaggedApi("android.hardware.radio.hd_radio_improved") @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public android.graphics.Bitmap getMetadataImage(int); method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean getMute(); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public java.util.Map<java.lang.String,java.lang.String> getParameters(@NonNull java.util.List<java.lang.String>); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]); @@ -14805,11 +14848,11 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean); method @FlaggedApi("com.android.internal.telephony.flags.enable_identifier_disclosure_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableCellularIdentifierDisclosureNotifications(boolean); - method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setEnableNullCipherNotifications(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setNrDualConnectivityState(int); + method @FlaggedApi("com.android.internal.telephony.flags.enable_modem_cipher_transparency") @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNullCipherNotificationsEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean); @@ -17171,38 +17214,38 @@ package android.telephony.satellite { } @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteManager { - method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getAllSatellitePlmnsForCarrier(int); - method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForIncomingDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback); - method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendSatelliteDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForIncomingDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback); field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_UNKNOWN = 0; // 0x0 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index b8b98a3cb3ba..c546d7a4cfe8 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -195,8 +195,11 @@ package android.app { method public void setTaskOverlay(boolean, boolean); } - public static final class ActivityOptions.LaunchCookie { + public static final class ActivityOptions.LaunchCookie implements android.os.Parcelable { ctor public ActivityOptions.LaunchCookie(); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.ActivityOptions.LaunchCookie> CREATOR; } public static interface ActivityOptions.OnAnimationFinishedListener { @@ -1262,9 +1265,9 @@ package android.credentials { } -package android.credentials.ui { +package android.credentials.selection { - public final class AuthenticationEntry implements android.os.Parcelable { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class AuthenticationEntry implements android.os.Parcelable { ctor public AuthenticationEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, int); ctor public AuthenticationEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, int, @NonNull android.content.Intent); method public int describeContents(); @@ -1274,32 +1277,47 @@ package android.credentials.ui { method @NonNull public int getStatus(); method @NonNull public String getSubkey(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.AuthenticationEntry> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.AuthenticationEntry> CREATOR; field public static final int STATUS_LOCKED = 0; // 0x0 field public static final int STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT = 1; // 0x1 field public static final int STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT = 2; // 0x2 } - public final class CreateCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable { - ctor public CreateCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.ui.Entry>, @Nullable android.credentials.ui.Entry); - method @Nullable public android.credentials.ui.Entry getRemoteEntry(); - method @NonNull public java.util.List<android.credentials.ui.Entry> getSaveEntries(); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.CreateCredentialProviderData> CREATOR; + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class BaseDialogResult implements android.os.Parcelable { + ctor public BaseDialogResult(@Nullable android.os.IBinder); + ctor protected BaseDialogResult(@NonNull android.os.Parcel); + method public static void addToBundle(@NonNull android.credentials.selection.BaseDialogResult, @NonNull android.os.Bundle); + method public int describeContents(); + method @Nullable public static android.credentials.selection.BaseDialogResult fromResultData(@NonNull android.os.Bundle); + method @Deprecated @Nullable public android.os.IBinder getRequestToken(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.BaseDialogResult> CREATOR; + field public static final int RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS = 1; // 0x1 + field public static final int RESULT_CODE_DATA_PARSING_FAILURE = 3; // 0x3 + field public static final int RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION = 2; // 0x2 + field public static final int RESULT_CODE_DIALOG_USER_CANCELED = 0; // 0x0 + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CreateCredentialProviderData extends android.credentials.selection.ProviderData implements android.os.Parcelable { + ctor public CreateCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.selection.Entry>, @Nullable android.credentials.selection.Entry); + method @Nullable public android.credentials.selection.Entry getRemoteEntry(); + method @NonNull public java.util.List<android.credentials.selection.Entry> getSaveEntries(); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.CreateCredentialProviderData> CREATOR; } - public static final class CreateCredentialProviderData.Builder { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public static final class CreateCredentialProviderData.Builder { ctor public CreateCredentialProviderData.Builder(@NonNull String); - method @NonNull public android.credentials.ui.CreateCredentialProviderData build(); - method @NonNull public android.credentials.ui.CreateCredentialProviderData.Builder setRemoteEntry(@Nullable android.credentials.ui.Entry); - method @NonNull public android.credentials.ui.CreateCredentialProviderData.Builder setSaveEntries(@NonNull java.util.List<android.credentials.ui.Entry>); + method @NonNull public android.credentials.selection.CreateCredentialProviderData build(); + method @NonNull public android.credentials.selection.CreateCredentialProviderData.Builder setRemoteEntry(@Nullable android.credentials.selection.Entry); + method @NonNull public android.credentials.selection.CreateCredentialProviderData.Builder setSaveEntries(@NonNull java.util.List<android.credentials.selection.Entry>); } - public final class DisabledProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class DisabledProviderData extends android.credentials.selection.ProviderData implements android.os.Parcelable { ctor public DisabledProviderData(@NonNull String); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.DisabledProviderData> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.DisabledProviderData> CREATOR; } - public final class Entry implements android.os.Parcelable { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class Entry implements android.os.Parcelable { ctor public Entry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice); ctor public Entry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice, @NonNull android.content.Intent); method public int describeContents(); @@ -1309,56 +1327,81 @@ package android.credentials.ui { method @NonNull public android.app.slice.Slice getSlice(); method @NonNull public String getSubkey(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.Entry> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.Entry> CREATOR; + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class FailureDialogResult extends android.credentials.selection.BaseDialogResult implements android.os.Parcelable { + ctor public FailureDialogResult(@Nullable android.os.IBinder, @Nullable String); + method public static void addToBundle(@NonNull android.credentials.selection.FailureDialogResult, @NonNull android.os.Bundle); + method @Nullable public static android.credentials.selection.FailureDialogResult fromResultData(@NonNull android.os.Bundle); + method @Nullable public String getErrorMessage(); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.FailureDialogResult> CREATOR; } - public final class GetCredentialProviderData extends android.credentials.ui.ProviderData implements android.os.Parcelable { - ctor public GetCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.ui.Entry>, @NonNull java.util.List<android.credentials.ui.Entry>, @NonNull java.util.List<android.credentials.ui.AuthenticationEntry>, @Nullable android.credentials.ui.Entry); - method @NonNull public java.util.List<android.credentials.ui.Entry> getActionChips(); - method @NonNull public java.util.List<android.credentials.ui.AuthenticationEntry> getAuthenticationEntries(); - method @NonNull public java.util.List<android.credentials.ui.Entry> getCredentialEntries(); - method @Nullable public android.credentials.ui.Entry getRemoteEntry(); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.GetCredentialProviderData> CREATOR; + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class GetCredentialProviderData extends android.credentials.selection.ProviderData implements android.os.Parcelable { + ctor public GetCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.selection.Entry>, @NonNull java.util.List<android.credentials.selection.Entry>, @NonNull java.util.List<android.credentials.selection.AuthenticationEntry>, @Nullable android.credentials.selection.Entry); + method @NonNull public java.util.List<android.credentials.selection.Entry> getActionChips(); + method @NonNull public java.util.List<android.credentials.selection.AuthenticationEntry> getAuthenticationEntries(); + method @NonNull public java.util.List<android.credentials.selection.Entry> getCredentialEntries(); + method @Nullable public android.credentials.selection.Entry getRemoteEntry(); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.GetCredentialProviderData> CREATOR; } - public static final class GetCredentialProviderData.Builder { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public static final class GetCredentialProviderData.Builder { ctor public GetCredentialProviderData.Builder(@NonNull String); - method @NonNull public android.credentials.ui.GetCredentialProviderData build(); - method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setActionChips(@NonNull java.util.List<android.credentials.ui.Entry>); - method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setAuthenticationEntries(@NonNull java.util.List<android.credentials.ui.AuthenticationEntry>); - method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setCredentialEntries(@NonNull java.util.List<android.credentials.ui.Entry>); - method @NonNull public android.credentials.ui.GetCredentialProviderData.Builder setRemoteEntry(@Nullable android.credentials.ui.Entry); + method @NonNull public android.credentials.selection.GetCredentialProviderData build(); + method @NonNull public android.credentials.selection.GetCredentialProviderData.Builder setActionChips(@NonNull java.util.List<android.credentials.selection.Entry>); + method @NonNull public android.credentials.selection.GetCredentialProviderData.Builder setAuthenticationEntries(@NonNull java.util.List<android.credentials.selection.AuthenticationEntry>); + method @NonNull public android.credentials.selection.GetCredentialProviderData.Builder setCredentialEntries(@NonNull java.util.List<android.credentials.selection.Entry>); + method @NonNull public android.credentials.selection.GetCredentialProviderData.Builder setRemoteEntry(@Nullable android.credentials.selection.Entry); } - public class IntentFactory { - method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.credentials.ui.RequestInfo, @NonNull java.util.ArrayList<android.credentials.ui.ProviderData>, @NonNull java.util.ArrayList<android.credentials.ui.DisabledProviderData>, @NonNull android.os.ResultReceiver); + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class IntentFactory { + method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.credentials.selection.RequestInfo, @NonNull java.util.ArrayList<android.credentials.selection.ProviderData>, @NonNull java.util.ArrayList<android.credentials.selection.DisabledProviderData>, @NonNull android.os.ResultReceiver); } - public abstract class ProviderData implements android.os.Parcelable { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public abstract class ProviderData implements android.os.Parcelable { ctor public ProviderData(@NonNull String); ctor protected ProviderData(@NonNull android.os.Parcel); method public int describeContents(); method @NonNull public String getProviderFlattenedComponentName(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final String EXTRA_DISABLED_PROVIDER_DATA_LIST = "android.credentials.ui.extra.DISABLED_PROVIDER_DATA_LIST"; - field public static final String EXTRA_ENABLED_PROVIDER_DATA_LIST = "android.credentials.ui.extra.ENABLED_PROVIDER_DATA_LIST"; + field public static final String EXTRA_DISABLED_PROVIDER_DATA_LIST = "android.credentials.selection.extra.DISABLED_PROVIDER_DATA_LIST"; + field public static final String EXTRA_ENABLED_PROVIDER_DATA_LIST = "android.credentials.selection.extra.ENABLED_PROVIDER_DATA_LIST"; } - public final class RequestInfo implements android.os.Parcelable { + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestInfo implements android.os.Parcelable { method public int describeContents(); method @NonNull public String getAppPackageName(); method @Nullable public android.credentials.CreateCredentialRequest getCreateCredentialRequest(); + method @NonNull public java.util.List<java.lang.String> getDefaultProviderIds(); method @Nullable public android.credentials.GetCredentialRequest getGetCredentialRequest(); + method @NonNull public java.util.List<java.lang.String> getRegistryProviderIds(); method @NonNull public android.os.IBinder getToken(); method @NonNull public String getType(); - method @NonNull public static android.credentials.ui.RequestInfo newCreateRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.CreateCredentialRequest, @NonNull String); - method @NonNull public static android.credentials.ui.RequestInfo newGetRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.GetCredentialRequest, @NonNull String); + method public boolean hasPermissionToOverrideDefault(); + method @NonNull public static android.credentials.selection.RequestInfo newCreateRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.CreateCredentialRequest, @NonNull String); + method @NonNull public static android.credentials.selection.RequestInfo newCreateRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.CreateCredentialRequest, @NonNull String, boolean, @NonNull java.util.List<java.lang.String>); + method @NonNull public static android.credentials.selection.RequestInfo newGetRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.GetCredentialRequest, @NonNull String, boolean); + method @NonNull public static android.credentials.selection.RequestInfo newGetRequestInfo(@NonNull android.os.IBinder, @NonNull android.credentials.GetCredentialRequest, @NonNull String); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.credentials.ui.RequestInfo> CREATOR; - field @NonNull public static final String EXTRA_REQUEST_INFO = "android.credentials.ui.extra.REQUEST_INFO"; - field @NonNull public static final String TYPE_CREATE = "android.credentials.ui.TYPE_CREATE"; - field @NonNull public static final String TYPE_GET = "android.credentials.ui.TYPE_GET"; - field @NonNull public static final String TYPE_UNDEFINED = "android.credentials.ui.TYPE_UNDEFINED"; + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.RequestInfo> CREATOR; + field @NonNull public static final String EXTRA_REQUEST_INFO = "android.credentials.selection.extra.REQUEST_INFO"; + field @NonNull public static final String TYPE_CREATE = "android.credentials.selection.TYPE_CREATE"; + field @NonNull public static final String TYPE_GET = "android.credentials.selection.TYPE_GET"; + field @NonNull public static final String TYPE_UNDEFINED = "android.credentials.selection.TYPE_UNDEFINED"; + } + + @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class UserSelectionDialogResult extends android.credentials.selection.BaseDialogResult implements android.os.Parcelable { + ctor public UserSelectionDialogResult(@Nullable android.os.IBinder, @NonNull String, @NonNull String, @NonNull String); + ctor public UserSelectionDialogResult(@Nullable android.os.IBinder, @NonNull String, @NonNull String, @NonNull String, @Nullable android.credentials.selection.ProviderPendingIntentResponse); + method public static void addToBundle(@NonNull android.credentials.selection.UserSelectionDialogResult, @NonNull android.os.Bundle); + method @Nullable public static android.credentials.selection.UserSelectionDialogResult fromResultData(@NonNull android.os.Bundle); + method @NonNull public String getEntryKey(); + method @NonNull public String getEntrySubkey(); + method @Nullable public android.credentials.selection.ProviderPendingIntentResponse getPendingIntentProviderResponse(); + method @NonNull public String getProviderId(); + field @NonNull public static final android.os.Parcelable.Creator<android.credentials.selection.UserSelectionDialogResult> CREATOR; } } @@ -2068,6 +2111,14 @@ package android.media.metrics { } +package android.media.projection { + + public final class MediaProjectionManager { + method @NonNull public android.content.Intent createScreenCaptureIntent(@Nullable android.app.ActivityOptions.LaunchCookie); + } + +} + package android.media.soundtrigger { public final class SoundTriggerInstrumentation { @@ -2603,7 +2654,6 @@ package android.os.storage { method @NonNull public static String convert(@NonNull java.util.UUID); method @Nullable public String getCloudMediaProvider(); method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int); - method public static boolean isUserKeyUnlocked(int); field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high"; field public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low"; field public static final String STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high"; @@ -3646,6 +3696,7 @@ package android.view { public interface WindowManager extends android.view.ViewManager { method public default int getDisplayImePolicy(int); + method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @Nullable public default android.os.IBinder getSurfaceControlInputClientToken(@NonNull android.view.SurfaceControl); method public static boolean hasWindowExtensionsEnabled(); method public default void holdLock(android.os.IBinder, int); method public default boolean isGlobalKey(int); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9d20f3c47bb5..6285eb3b2096 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -107,6 +107,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -4440,8 +4441,7 @@ public class ActivityManager { * is used here, you will receive a call each time a uids importance transitions between * being <= {@link RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE} and * > {@link RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE}. - * @param uids The UIDs that this listener is interested with. A {@code null} value means - * all UIDs will be monitored by this listener, this will be equivalent to the + * @param uids The UIDs that this listener is interested with. * {@link #addOnUidImportanceListener(OnUidImportanceListener, int)} in this case. * * <p>Calling this API with the same instance of {@code listener} without @@ -4456,7 +4456,9 @@ public class ActivityManager { @SuppressLint("SamShouldBeLast") @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(@NonNull OnUidImportanceListener listener, - @RunningAppProcessInfo.Importance int importanceCutpoint, @Nullable int[] uids) { + @RunningAppProcessInfo.Importance int importanceCutpoint, @NonNull int[] uids) { + Objects.requireNonNull(listener); + Objects.requireNonNull(uids); addOnUidImportanceListenerInternal(listener, importanceCutpoint, uids); } diff --git a/core/java/android/app/ActivityOptions.aidl b/core/java/android/app/ActivityOptions.aidl index bd5cd88959a3..2d4a85f4f6f4 100644 --- a/core/java/android/app/ActivityOptions.aidl +++ b/core/java/android/app/ActivityOptions.aidl @@ -17,4 +17,7 @@ package android.app; /** @hide */ -parcelable ActivityOptions.SceneTransitionInfo;
\ No newline at end of file +parcelable ActivityOptions.SceneTransitionInfo; + +/** @hide */ +parcelable ActivityOptions.LaunchCookie;
\ No newline at end of file diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 57fca74e7e59..111895e3053b 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -184,6 +184,12 @@ public class ActivityOptions extends ComponentOptions { public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener"; /** + * Callback for when animation is aborted. + * @hide + */ + private static final String KEY_ANIM_ABORT_LISTENER = "android:activity.animAbortListener"; + + /** * Specific a theme for a splash screen window. * @hide */ @@ -459,6 +465,7 @@ public class ActivityOptions extends ComponentOptions { private int mHeight; private IRemoteCallback mAnimationStartedListener; private IRemoteCallback mAnimationFinishedListener; + private IRemoteCallback mAnimationAbortListener; private SceneTransitionInfo mSceneTransitionInfo; private PendingIntent mUsageTimeReport; private int mLaunchDisplayId = INVALID_DISPLAY; @@ -716,6 +723,14 @@ public class ActivityOptions extends ComponentOptions { } /** + * Callback for finding out when the given animation is finished + * @hide + */ + public void setOnAnimationFinishedListener(IRemoteCallback listener) { + mAnimationFinishedListener = listener; + } + + /** * Callback for finding out when the given animation has drawn its last frame. * @hide */ @@ -728,6 +743,14 @@ public class ActivityOptions extends ComponentOptions { } /** + * Callback for finding out when the given animation is aborted + * @hide + */ + public void setOnAnimationAbortListener(IRemoteCallback listener) { + mAnimationAbortListener = listener; + } + + /** * Create an ActivityOptions specifying an animation where the new * activity is scaled from a small originating area of the screen to * its final full representation. @@ -1327,6 +1350,8 @@ public class ActivityOptions extends ComponentOptions { KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE, MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED); mDisableStartingWindow = opts.getBoolean(KEY_DISABLE_STARTING_WINDOW); + mAnimationAbortListener = IRemoteCallback.Stub.asInterface( + opts.getBinder(KEY_ANIM_ABORT_LISTENER)); } /** @@ -1427,11 +1452,15 @@ public class ActivityOptions extends ComponentOptions { /** @hide */ public void abort() { - if (mAnimationStartedListener != null) { + sendResultIgnoreErrors(mAnimationStartedListener, null); + sendResultIgnoreErrors(mAnimationAbortListener, null); + } + + private void sendResultIgnoreErrors(IRemoteCallback callback, Bundle data) { + if (callback != null) { try { - mAnimationStartedListener.sendResult(null); - } catch (RemoteException e) { - } + callback.sendResult(data); + } catch (RemoteException e) { } } } @@ -1929,14 +1958,87 @@ public class ActivityOptions extends ComponentOptions { */ @SuppressLint("UnflaggedApi") @TestApi - public static final class LaunchCookie { + public static final class LaunchCookie implements Parcelable { + /** @hide */ + public final IBinder binder; + + /** @hide */ + @SuppressLint("UnflaggedApi") + @TestApi + public LaunchCookie() { + binder = new Binder(); + } + + /** @hide */ + public LaunchCookie(@Nullable String descriptor) { + binder = new Binder(descriptor); + } + + private LaunchCookie(Parcel in) { + this.binder = in.readStrongBinder(); + } + + /** @hide */ + @SuppressLint("UnflaggedApi") + @TestApi + @Override + public int describeContents() { + return 0; + } + /** @hide */ - public final IBinder binder = new Binder(); + @SuppressLint("UnflaggedApi") + @TestApi + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(binder); + } + + /** @hide */ + public static LaunchCookie readFromParcel(@NonNull Parcel in) { + return new LaunchCookie(in); + } + + /** @hide */ + public static void writeToParcel(@Nullable LaunchCookie launchCookie, Parcel out) { + if (launchCookie != null) { + launchCookie.writeToParcel(out, 0); + } else { + out.writeStrongBinder(null); + } + } /** @hide */ @SuppressLint("UnflaggedApi") @TestApi - public LaunchCookie() {} + @NonNull + public static final Parcelable.Creator<LaunchCookie> CREATOR = + new Parcelable.Creator<LaunchCookie>() { + + @Override + public LaunchCookie createFromParcel(Parcel source) { + return new LaunchCookie(source); + } + + @Override + public LaunchCookie[] newArray(int size) { + return new LaunchCookie[size]; + } + }; + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof LaunchCookie) { + LaunchCookie other = (LaunchCookie) obj; + return binder == other.binder; + } + return false; + } + + @Override + public int hashCode() { + return binder.hashCode(); + } } /** @@ -2110,12 +2212,7 @@ public class ActivityOptions extends ComponentOptions { mCustomExitResId = otherOptions.mCustomExitResId; mCustomBackgroundColor = otherOptions.mCustomBackgroundColor; mThumbnail = null; - if (mAnimationStartedListener != null) { - try { - mAnimationStartedListener.sendResult(null); - } catch (RemoteException e) { - } - } + sendResultIgnoreErrors(mAnimationStartedListener, null); mAnimationStartedListener = otherOptions.mAnimationStartedListener; break; case ANIM_CUSTOM_IN_PLACE: @@ -2126,12 +2223,7 @@ public class ActivityOptions extends ComponentOptions { mStartY = otherOptions.mStartY; mWidth = otherOptions.mWidth; mHeight = otherOptions.mHeight; - if (mAnimationStartedListener != null) { - try { - mAnimationStartedListener.sendResult(null); - } catch (RemoteException e) { - } - } + sendResultIgnoreErrors(mAnimationStartedListener, null); mAnimationStartedListener = null; break; case ANIM_THUMBNAIL_SCALE_UP: @@ -2143,12 +2235,7 @@ public class ActivityOptions extends ComponentOptions { mStartY = otherOptions.mStartY; mWidth = otherOptions.mWidth; mHeight = otherOptions.mHeight; - if (mAnimationStartedListener != null) { - try { - mAnimationStartedListener.sendResult(null); - } catch (RemoteException e) { - } - } + sendResultIgnoreErrors(mAnimationStartedListener, null); mAnimationStartedListener = otherOptions.mAnimationStartedListener; break; case ANIM_SCENE_TRANSITION: @@ -2165,6 +2252,9 @@ public class ActivityOptions extends ComponentOptions { mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter; mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams; mIsEligibleForLegacyPermissionPrompt = otherOptions.mIsEligibleForLegacyPermissionPrompt; + + sendResultIgnoreErrors(mAnimationAbortListener, null); + mAnimationAbortListener = otherOptions.mAnimationAbortListener; } /** @@ -2363,6 +2453,8 @@ public class ActivityOptions extends ComponentOptions { if (mDisableStartingWindow) { b.putBoolean(KEY_DISABLE_STARTING_WINDOW, mDisableStartingWindow); } + b.putBinder(KEY_ANIM_ABORT_LISTENER, + mAnimationAbortListener != null ? mAnimationAbortListener.asBinder() : null); return b; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1bdbd4c50634..5d2a26e13ee7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -319,6 +319,10 @@ public final class ActivityThread extends ClientTransactionHandler public static final int SERVICE_DONE_EXECUTING_START = 1; /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */ public static final int SERVICE_DONE_EXECUTING_STOP = 2; + /** Type for IActivityManager.serviceDoneExecuting: done with an onRebind call */ + public static final int SERVICE_DONE_EXECUTING_REBIND = 3; + /** Type for IActivityManager.serviceDoneExecuting: done with an onUnbind call */ + public static final int SERVICE_DONE_EXECUTING_UNBIND = 4; /** Use foreground GC policy (less pause time) and higher JIT weight. */ private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0; @@ -4882,7 +4886,7 @@ public final class ActivityThread extends ClientTransactionHandler mServices.put(data.token, service); try { ActivityManager.getService().serviceDoneExecuting( - data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); + data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4913,7 +4917,7 @@ public final class ActivityThread extends ClientTransactionHandler } else { s.onRebind(data.intent); ActivityManager.getService().serviceDoneExecuting( - data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); + data.token, SERVICE_DONE_EXECUTING_REBIND, 0, 0, data.intent); } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); @@ -4943,7 +4947,7 @@ public final class ActivityThread extends ClientTransactionHandler data.token, data.intent, doRebind); } else { ActivityManager.getService().serviceDoneExecuting( - data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); + data.token, SERVICE_DONE_EXECUTING_UNBIND, 0, 0, data.intent); } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); @@ -5057,7 +5061,7 @@ public final class ActivityThread extends ClientTransactionHandler try { ActivityManager.getService().serviceDoneExecuting( - data.token, SERVICE_DONE_EXECUTING_START, data.startId, res); + data.token, SERVICE_DONE_EXECUTING_START, data.startId, res, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -5089,7 +5093,7 @@ public final class ActivityThread extends ClientTransactionHandler try { ActivityManager.getService().serviceDoneExecuting( - token, SERVICE_DONE_EXECUTING_STOP, 0, 0); + token, SERVICE_DONE_EXECUTING_STOP, 0, 0, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 00c4b0f6515f..bb666e65d6fb 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1555,9 +1555,24 @@ public class AppOpsManager { */ public static final int OP_RUN_BACKUP_JOBS = AppProtoEnums.APP_OP_RUN_BACKUP_JOBS; + /** + * Whether the app has enabled to receive the icon overlay for fetching archived apps. + * + * @hide + */ + public static final int OP_ARCHIVE_ICON_OVERLAY = AppProtoEnums.APP_OP_ARCHIVE_ICON_OVERLAY; + + /** + * Whether the app has enabled compatibility support for unarchival. + * + * @hide + */ + public static final int OP_UNARCHIVAL_CONFIRMATION = + AppProtoEnums.APP_OP_UNARCHIVAL_CONFIRMATION; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 145; + public static final int _NUM_OP = 147; /** * All app ops represented as strings. @@ -1708,6 +1723,8 @@ public class AppOpsManager { OPSTR_RESERVED_FOR_TESTING, OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER, OPSTR_RUN_BACKUP_JOBS, + OPSTR_ARCHIVE_ICON_OVERLAY, + OPSTR_UNARCHIVAL_CONFIRMATION, }) public @interface AppOpString {} @@ -2048,6 +2065,20 @@ public class AppOpsManager { public static final String OPSTR_MEDIA_ROUTING_CONTROL = "android:media_routing_control"; /** + * Whether the app has enabled to receive the icon overlay for fetching archived apps. + * + * @hide + */ + public static final String OPSTR_ARCHIVE_ICON_OVERLAY = "android:archive_icon_overlay"; + + /** + * Whether the app has enabled compatibility support for unarchival. + * + * @hide + */ + public static final String OPSTR_UNARCHIVAL_CONFIRMATION = "android:unarchival_support"; + + /** * AppOp granted to apps that we are started via {@code am instrument -e --no-isolated-storage} * * <p>MediaProvider is the only component (outside of system server) that should care about this @@ -2520,6 +2551,8 @@ public class AppOpsManager { OP_MEDIA_ROUTING_CONTROL, OP_READ_SYSTEM_GRAMMATICAL_GENDER, OP_RUN_BACKUP_JOBS, + OP_ARCHIVE_ICON_OVERLAY, + OP_UNARCHIVAL_CONFIRMATION, }; static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{ @@ -2979,6 +3012,12 @@ public class AppOpsManager { .build(), new AppOpInfo.Builder(OP_RUN_BACKUP_JOBS, OPSTR_RUN_BACKUP_JOBS, "RUN_BACKUP_JOBS") .setPermission(Manifest.permission.RUN_BACKUP_JOBS).build(), + new AppOpInfo.Builder(OP_ARCHIVE_ICON_OVERLAY, OPSTR_ARCHIVE_ICON_OVERLAY, + "ARCHIVE_ICON_OVERLAY") + .setDefaultMode(MODE_ALLOWED).build(), + new AppOpInfo.Builder(OP_UNARCHIVAL_CONFIRMATION, OPSTR_UNARCHIVAL_CONFIRMATION, + "UNARCHIVAL_CONFIRMATION") + .setDefaultMode(MODE_ALLOWED).build(), }; // The number of longs needed to form a full bitmask of app ops @@ -3113,7 +3152,7 @@ public class AppOpsManager { /** * Retrieve the permission associated with an operation, or null if there is not one. - * + * @param op The operation name. * * @hide diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 34c44f9489d5..4f1db7d3784a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -4032,7 +4032,8 @@ public class ApplicationPackageManager extends PackageManager { private Drawable getArchivedAppIcon(String packageName) { try { return new BitmapDrawable(null, - mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId()))); + mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId()), + mContext.getPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ed00d9c1ddde..b0f6c4656dd1 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -240,7 +240,7 @@ class ContextImpl extends Context { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final String mOpPackageName; private final @NonNull ContextParams mParams; - private final @NonNull AttributionSource mAttributionSource; + private @NonNull AttributionSource mAttributionSource; private final @NonNull ResourcesManager mResourcesManager; @UnsupportedAppUsage @@ -2630,10 +2630,9 @@ class ContextImpl extends Context { flags | CONTEXT_REGISTER_PACKAGE); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - null, mToken, new UserHandle(UserHandle.getUserId(application.uid)), - flags, null, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), null, + mToken, new UserHandle(UserHandle.getUserId(application.uid)), flags, null, + null, mDeviceId, mIsExplicitDeviceId); final int displayId = getDisplayId(); final Integer overrideDisplayId = mForceDisplayOverrideInResources @@ -2679,18 +2678,16 @@ class ContextImpl extends Context { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. return new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - null, mToken, user, flags, null, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), null, + mToken, user, flags, null, null, mDeviceId, mIsExplicitDeviceId); } LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - null, mToken, user, flags, null, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), null, + mToken, user, flags, null, null, mDeviceId, mIsExplicitDeviceId); final int displayId = getDisplayId(); final Integer overrideDisplayId = mForceDisplayOverrideInResources @@ -2729,9 +2726,8 @@ class ContextImpl extends Context { final String[] paths = mPackageInfo.getSplitPaths(splitName); final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - splitName, mToken, mUser, mFlags, classLoader, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), splitName, + mToken, mUser, mFlags, classLoader, null, mDeviceId, mIsExplicitDeviceId); context.setResources(ResourcesManager.getInstance().getResources( mToken, @@ -2765,9 +2761,9 @@ class ContextImpl extends Context { } ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - mSplitName, mToken, mUser, mFlags, mClassLoader, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, + mToken, mUser, mFlags, mClassLoader, null, mDeviceId, + mIsExplicitDeviceId); context.mIsConfigurationBasedContext = true; final int displayId = getDisplayId(); @@ -2786,9 +2782,8 @@ class ContextImpl extends Context { } ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - mSplitName, mToken, mUser, mFlags, mClassLoader, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, + mToken, mUser, mFlags, mClassLoader, null, mDeviceId, mIsExplicitDeviceId); final int displayId = display.getDisplayId(); @@ -2833,14 +2828,9 @@ class ContextImpl extends Context { } } - ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - mSplitName, mToken, mUser, mFlags, mClassLoader, null); - - context.mDeviceId = deviceId; - context.mIsExplicitDeviceId = true; - return context; + return new ContextImpl(this, mMainThread, mPackageInfo, mParams, + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, + mToken, mUser, mFlags, mClassLoader, null, deviceId, true); } @NonNull @@ -2926,9 +2916,8 @@ class ContextImpl extends Context { @UiContext ContextImpl createWindowContextBase(@NonNull IBinder token, int displayId) { ContextImpl baseContext = new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - mSplitName, token, mUser, mFlags, mClassLoader, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, + token, mUser, mFlags, mClassLoader, null, mDeviceId, mIsExplicitDeviceId); // Window contexts receive configurations directly from the server and as such do not // need to override their display in ResourcesManager. baseContext.mForceDisplayOverrideInResources = false; @@ -2979,7 +2968,8 @@ class ContextImpl extends Context { public Context createContext(@NonNull ContextParams contextParams) { return new ContextImpl(this, mMainThread, mPackageInfo, contextParams, contextParams.getAttributionTag(), contextParams.getNextAttributionSource(), - mSplitName, mToken, mUser, mFlags, mClassLoader, null); + mSplitName, mToken, mUser, mFlags, mClassLoader, null, mDeviceId, + mIsExplicitDeviceId); } @Override @@ -2993,9 +2983,8 @@ class ContextImpl extends Context { final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE) | Context.CONTEXT_DEVICE_PROTECTED_STORAGE; return new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - mSplitName, mToken, mUser, flags, mClassLoader, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, + mToken, mUser, flags, mClassLoader, null, mDeviceId, mIsExplicitDeviceId); } @Override @@ -3003,9 +2992,8 @@ class ContextImpl extends Context { final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE) | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE; return new ContextImpl(this, mMainThread, mPackageInfo, mParams, - mAttributionSource.getAttributionTag(), - mAttributionSource.getNext(), - mSplitName, mToken, mUser, flags, mClassLoader, null); + mAttributionSource.getAttributionTag(), mAttributionSource.getNext(), mSplitName, + mToken, mUser, flags, mClassLoader, null, mDeviceId, mIsExplicitDeviceId); } @Override @@ -3098,6 +3086,7 @@ class ContextImpl extends Context { int deviceId = vdm.getDeviceIdForDisplayId(displayId); if (deviceId != mDeviceId) { mDeviceId = deviceId; + mAttributionSource = mAttributionSource.withDeviceId(mDeviceId); notifyOnDeviceChangedListeners(mDeviceId); } } @@ -3298,7 +3287,8 @@ class ContextImpl extends Context { static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, - ContextParams.EMPTY, null, null, null, null, null, 0, null, null); + ContextParams.EMPTY, null, null, null, null, null, 0, null, null, + DEVICE_ID_DEFAULT, false); context.setResources(packageInfo.getResources()); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetrics()); @@ -3333,7 +3323,8 @@ class ContextImpl extends Context { String opPackageName) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, - ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName); + ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName, + DEVICE_ID_DEFAULT, false); context.setResources(packageInfo.getResources()); context.mContextType = isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI : CONTEXT_TYPE_NON_UI; @@ -3371,7 +3362,7 @@ class ContextImpl extends Context { ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY, attributionTag, null, activityInfo.splitName, activityToken, null, 0, classLoader, - null); + null, DEVICE_ID_DEFAULT, false); context.mContextType = CONTEXT_TYPE_ACTIVITY; context.mIsConfigurationBasedContext = true; @@ -3407,7 +3398,8 @@ class ContextImpl extends Context { @NonNull LoadedApk packageInfo, @NonNull ContextParams params, @Nullable String attributionTag, @Nullable AttributionSource nextAttributionSource, @Nullable String splitName, @Nullable IBinder token, @Nullable UserHandle user, - int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) { + int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName, + int deviceId, boolean isExplicitDeviceId) { mOuterContext = this; // If creator didn't specify which storage to use, use the default // location for application. @@ -3437,13 +3429,18 @@ class ContextImpl extends Context { String opPackageName; + mDeviceId = deviceId; + mIsExplicitDeviceId = isExplicitDeviceId; + if (container != null) { mBasePackageName = container.mBasePackageName; opPackageName = container.mOpPackageName; setResources(container.mResources); mDisplay = container.mDisplay; - mDeviceId = container.mDeviceId; - mIsExplicitDeviceId = container.mIsExplicitDeviceId; + if (!isExplicitDeviceId) { + mIsExplicitDeviceId = container.mIsExplicitDeviceId; + mDeviceId = container.mDeviceId; + } mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources; mIsConfigurationBasedContext = container.mIsConfigurationBasedContext; mContextType = container.mContextType; @@ -3466,17 +3463,18 @@ class ContextImpl extends Context { mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName; mParams = Objects.requireNonNull(params); mAttributionSource = createAttributionSource(attributionTag, nextAttributionSource, - params.getRenouncedPermissions(), params.shouldRegisterAttributionSource()); + params.getRenouncedPermissions(), params.shouldRegisterAttributionSource(), mDeviceId); mContentResolver = new ApplicationContentResolver(this, mainThread); } private @NonNull AttributionSource createAttributionSource(@Nullable String attributionTag, @Nullable AttributionSource nextAttributionSource, - @Nullable Set<String> renouncedPermissions, boolean shouldRegister) { + @Nullable Set<String> renouncedPermissions, boolean shouldRegister, + int deviceId) { AttributionSource attributionSource = new AttributionSource(Process.myUid(), Process.myPid(), mOpPackageName, attributionTag, (renouncedPermissions != null) ? renouncedPermissions.toArray(new String[0]) : null, - getDeviceId(), nextAttributionSource); + deviceId, nextAttributionSource); // If we want to access protected data on behalf of another app we need to // tell the OS that we opt in to participate in the attribution chain. if (nextAttributionSource != null || shouldRegister) { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 47403d26c907..b063d04655f5 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -294,7 +294,8 @@ interface IActivityManager { @UnsupportedAppUsage ParceledListSlice getRecentTasks(int maxNum, int flags, int userId); @UnsupportedAppUsage - oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res); + oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res, + in Intent intent); /** @deprecated Use {@link #getIntentSenderWithFeature} instead */ @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link PendingIntent#getIntentSender()} instead") IIntentSender getIntentSender(int type, in String packageName, in IBinder token, diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 2162e3a77f15..68512b8bd771 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -79,6 +79,7 @@ import java.util.concurrent.TimeoutException; * implementation is described to the system through an AndroidManifest.xml's * <instrumentation> tag. */ +@android.ravenwood.annotation.RavenwoodKeepPartialClass public class Instrumentation { /** @@ -132,6 +133,7 @@ public class Instrumentation { private UiAutomation mUiAutomation; private final Object mAnimationCompleteLock = new Object(); + @android.ravenwood.annotation.RavenwoodKeep public Instrumentation() { } @@ -142,6 +144,7 @@ public class Instrumentation { * reflection, but it will serve as noticeable discouragement from * doing such a thing. */ + @android.ravenwood.annotation.RavenwoodReplace private void checkInstrumenting(String method) { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. @@ -151,6 +154,11 @@ public class Instrumentation { } } + private void checkInstrumenting$ravenwood(String method) { + // At the moment, Ravenwood doesn't attach a Context, but we're only ever + // running code as part of tests, so we continue quietly + } + /** * Returns if it is being called in an instrumentation environment. * @@ -2504,6 +2512,7 @@ public class Instrumentation { * Takes control of the execution of messages on the specified looper until * {@link TestLooperManager#release} is called. */ + @android.ravenwood.annotation.RavenwoodKeep public TestLooperManager acquireLooperManager(Looper looper) { checkInstrumenting("acquireLooperManager"); return new TestLooperManager(looper); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 0261f0a02174..1ac08ac4cd24 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -179,6 +179,14 @@ public final class PendingIntent implements Parcelable { @Overridable public static final long BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT = 236704164L; + /** + * Validate options passed in as bundle. + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public static final long PENDING_INTENT_OPTIONS_CHECK = 320664730L; + /** @hide */ @IntDef(flag = true, value = { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index d7554137fa5b..397b63fe6a30 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -210,6 +210,7 @@ import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.print.IPrintManager; import android.print.PrintManager; +import android.provider.ContactKeysManager; import android.safetycenter.SafetyCenterFrameworkInitializer; import android.scheduling.SchedulingFrameworkInitializer; import android.security.FileIntegrityManager; @@ -1604,6 +1605,18 @@ public final class SystemServiceRegistry { } }); + registerService(Context.CONTACT_KEYS_SERVICE, ContactKeysManager.class, + new CachedServiceFetcher<ContactKeysManager>() { + @Override + public ContactKeysManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + if (!android.provider.Flags.userKeys()) { + throw new ServiceNotFoundException( + "ContactKeysManager is not supported"); + } + return new ContactKeysManager(ctx); + }}); + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 5c42b0ed975a..86d0125fd7a2 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -53,6 +53,7 @@ import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; +import static android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -61,6 +62,7 @@ import android.accounts.Account; import android.annotation.BroadcastBehavior; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -4092,6 +4094,29 @@ public class DevicePolicyManager { return MTE_NOT_CONTROLLED_BY_POLICY; } + /** Indicates that content protection is not controlled by policy, allowing user to choose. */ + @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED) + public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; + + /** Indicates that content protection is controlled and disabled by a policy. */ + @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED) + public static final int CONTENT_PROTECTION_DISABLED = 1; + + /** Indicates that content protection is controlled and enabled by a policy. */ + @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED) + public static final int CONTENT_PROTECTION_ENABLED = 2; + + /** @hide */ + @IntDef( + prefix = {"CONTENT_PROTECTION_"}, + value = { + CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY, + CONTENT_PROTECTION_DISABLED, + CONTENT_PROTECTION_ENABLED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ContentProtectionPolicy {} + /** * This object is a single place to tack on invalidation and disable calls. All * binder caches in this class derive from this Config, so all can be invalidated or diff --git a/core/java/android/app/background_install_control_manager.aconfig b/core/java/android/app/background_install_control_manager.aconfig index 029b93ab4534..4473b9523f1b 100644 --- a/core/java/android/app/background_install_control_manager.aconfig +++ b/core/java/android/app/background_install_control_manager.aconfig @@ -1,7 +1,7 @@ package: "android.app" flag { - namespace: "background_install_control" + namespace: "preload_safety" name: "bic_client" description: "System API for background install control." is_fixed_read_only: true diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index ec181dac6b36..1f19f817a0b3 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -907,7 +907,10 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW private InteractionHandler getHandler(InteractionHandler handler) { return (view, pendingIntent, response) -> { - AppWidgetManager.getInstance(mContext).noteAppWidgetTapped(mAppWidgetId); + AppWidgetManager manager = AppWidgetManager.getInstance(mContext); + if (manager != null) { + manager.noteAppWidgetTapped(mAppWidgetId); + } if (handler != null) { return handler.onInteraction(view, pendingIntent, response); } else { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 67a3627a399f..b8d754348211 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3657,8 +3657,8 @@ public abstract class Context { * On Android {@link android.os.Build.VERSION_CODES#S} and later, * if the application is in a state where the service * can not be started (such as not in the foreground in a state when services are allowed), - * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown - * This excemption extends {@link IllegalStateException}, so apps can + * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown. + * This exception extends {@link IllegalStateException}, so apps can * use {@code catch (IllegalStateException)} to catch both. * * @see #startForegroundService(Intent) @@ -4243,6 +4243,7 @@ public abstract class Context { GRAMMATICAL_INFLECTION_SERVICE, SECURITY_STATE_SERVICE, //@hide: ECM_ENHANCED_CONFIRMATION_SERVICE, + CONTACT_KEYS_SERVICE, }) @Retention(RetentionPolicy.SOURCE) @@ -6542,6 +6543,16 @@ public abstract class Context { public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation"; /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.provider.ContactKeysManager} to managing contact keys. + * + * @see #getSystemService(String) + * @see android.provider.ContactKeysManager + */ + @FlaggedApi(android.provider.Flags.FLAG_USER_KEYS) + public static final String CONTACT_KEYS_SERVICE = "contact_keys"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index ad3acd713c6b..79af65a3a3e5 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -16,11 +16,13 @@ package android.content; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.pm.Flags; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -175,6 +177,7 @@ public class IntentFilter implements Parcelable { private static final String ACTION_STR = "action"; private static final String AUTO_VERIFY_STR = "autoVerify"; private static final String EXTRAS_STR = "extras"; + private static final String URI_RELATIVE_FILTER_GROUP_STR = "uriRelativeFilterGroup"; private static final int[] EMPTY_INT_ARRAY = new int[0]; private static final long[] EMPTY_LONG_ARRAY = new long[0]; @@ -324,6 +327,7 @@ public class IntentFilter implements Parcelable { private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null; private ArrayList<AuthorityEntry> mDataAuthorities = null; private ArrayList<PatternMatcher> mDataPaths = null; + private ArrayList<UriRelativeFilterGroup> mUriRelativeFilterGroups = null; private ArrayList<String> mStaticDataTypes = null; private ArrayList<String> mDataTypes = null; private ArrayList<String> mMimeGroups = null; @@ -520,6 +524,10 @@ public class IntentFilter implements Parcelable { if (o.mDataPaths != null) { mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths); } + if (o.mUriRelativeFilterGroups != null) { + mUriRelativeFilterGroups = + new ArrayList<UriRelativeFilterGroup>(o.mUriRelativeFilterGroups); + } if (o.mMimeGroups != null) { mMimeGroups = new ArrayList<String>(o.mMimeGroups); } @@ -1563,6 +1571,63 @@ public class IntentFilter implements Parcelable { } /** + * Add a new URI relative filter group to match against the Intent data. The + * intent filter must include one or more schemes (via {@link #addDataScheme}) + * <em>and</em> one or more authorities (via {@link #addDataAuthority}) for + * the group to be considered. + * + * <p>Groups will be matched in the order they were added and matching will only + * be done if no data paths match or if none are included. If both data paths and + * groups are not included, then only the scheme/authority must match.</p> + * + * @param group A {@link UriRelativeFilterGroup} to match the URI. + * + * @see UriRelativeFilterGroup + * @see #matchData + * @see #addDataScheme + * @see #addDataAuthority + */ + @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) + public final void addUriRelativeFilterGroup(@NonNull UriRelativeFilterGroup group) { + Objects.requireNonNull(group); + if (mUriRelativeFilterGroups == null) { + mUriRelativeFilterGroups = new ArrayList<>(); + } + mUriRelativeFilterGroups.add(group); + } + + /** + * Return the number of URI relative filter groups in the intent filter. + */ + @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) + public final int countUriRelativeFilterGroups() { + return mUriRelativeFilterGroups == null ? 0 : mUriRelativeFilterGroups.size(); + } + + /** + * Return a URI relative filter group in the intent filter. + * + * <p>Note: use of this method will result in a NullPointerException + * if no groups exists for this intent filter.</p> + * + * @param index index of the element to return + * @throws IndexOutOfBoundsException if index is out of range + */ + @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) + @NonNull + public final UriRelativeFilterGroup getUriRelativeFilterGroup(int index) { + return mUriRelativeFilterGroups.get(index); + } + + /** + * Removes all existing URI relative filter groups in the intent filter. + */ + @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) + public final void clearUriRelativeFilterGroups() { + mUriRelativeFilterGroups = null; + } + + /** * Match this intent filter against the given Intent data. This ignores * the data scheme -- unlike {@link #matchData}, the authority will match * regardless of whether there is a matching scheme. @@ -1677,12 +1742,24 @@ public class IntentFilter implements Parcelable { int authMatch = matchDataAuthority(data, wildcardSupported); if (authMatch >= 0) { final ArrayList<PatternMatcher> paths = mDataPaths; - if (paths == null) { - match = authMatch; - } else if (hasDataPath(data.getPath(), wildcardSupported)) { - match = MATCH_CATEGORY_PATH; + final ArrayList<UriRelativeFilterGroup> groups = mUriRelativeFilterGroups; + if (Flags.relativeReferenceIntentFilters()) { + if (paths == null && groups == null) { + match = authMatch; + } else if (hasDataPath(data.getPath(), wildcardSupported) + || matchRelRefGroups(data)) { + match = MATCH_CATEGORY_PATH; + } else { + return NO_MATCH_DATA; + } } else { - return NO_MATCH_DATA; + if (paths == null) { + match = authMatch; + } else if (hasDataPath(data.getPath(), wildcardSupported)) { + match = MATCH_CATEGORY_PATH; + } else { + return NO_MATCH_DATA; + } } } else { return NO_MATCH_DATA; @@ -1726,6 +1803,19 @@ public class IntentFilter implements Parcelable { return match + MATCH_ADJUSTMENT_NORMAL; } + private boolean matchRelRefGroups(Uri data) { + if (mUriRelativeFilterGroups == null) { + return false; + } + for (int i = 0; i < mUriRelativeFilterGroups.size(); i++) { + UriRelativeFilterGroup group = mUriRelativeFilterGroups.get(i); + if (group.matchData(data)) { + return group.getAction() == UriRelativeFilterGroup.ACTION_ALLOW; + } + } + return false; + } + /** * Add a new Intent category to match against. The semantics of * categories is the opposite of actions -- an Intent includes the @@ -2486,6 +2576,12 @@ public class IntentFilter implements Parcelable { } serializer.endTag(null, EXTRAS_STR); } + if (Flags.relativeReferenceIntentFilters()) { + N = countUriRelativeFilterGroups(); + for (int i = 0; i < N; i++) { + mUriRelativeFilterGroups.get(i).writeToXml(serializer); + } + } } /** @@ -2614,6 +2710,9 @@ public class IntentFilter implements Parcelable { } } else if (tagName.equals(EXTRAS_STR)) { mExtras = PersistableBundle.restoreFromXml(parser); + } else if (Flags.relativeReferenceIntentFilters() + && URI_RELATIVE_FILTER_GROUP_STR.equals(tagName)) { + addUriRelativeFilterGroup(new UriRelativeFilterGroup(parser)); } else { Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName); } @@ -2680,6 +2779,12 @@ public class IntentFilter implements Parcelable { if (mExtras != null) { mExtras.dumpDebug(proto, IntentFilterProto.EXTRAS); } + if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) { + Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator(); + while (it.hasNext()) { + it.next().dumpDebug(proto, IntentFilterProto.URI_RELATIVE_FILTER_GROUPS); + } + } proto.end(token); } @@ -2744,6 +2849,15 @@ public class IntentFilter implements Parcelable { du.println(sb.toString()); } } + if (mUriRelativeFilterGroups != null) { + Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator(); + while (it.hasNext()) { + sb.setLength(0); + sb.append(prefix); sb.append("UriRelativeFilterGroup: \""); + sb.append(it.next()); sb.append("\""); + du.println(sb.toString()); + } + } if (mStaticDataTypes != null) { Iterator<String> it = mStaticDataTypes.iterator(); while (it.hasNext()) { @@ -2883,6 +2997,15 @@ public class IntentFilter implements Parcelable { } else { dest.writeInt(0); } + if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) { + final int N = mUriRelativeFilterGroups.size(); + dest.writeInt(N); + for (int i = 0; i < N; i++) { + mUriRelativeFilterGroups.get(i).writeToParcel(dest, flags); + } + } else { + dest.writeInt(0); + } } /** @@ -2989,6 +3112,13 @@ public class IntentFilter implements Parcelable { if (source.readInt() != 0) { mExtras = PersistableBundle.CREATOR.createFromParcel(source); } + N = source.readInt(); + if (Flags.relativeReferenceIntentFilters() && N > 0) { + mUriRelativeFilterGroups = new ArrayList<UriRelativeFilterGroup>(N); + for (int i = 0; i < N; i++) { + mUriRelativeFilterGroups.add(new UriRelativeFilterGroup(source)); + } + } } private boolean hasPartialTypes() { diff --git a/core/java/android/content/UriRelativeFilter.java b/core/java/android/content/UriRelativeFilter.java new file mode 100644 index 000000000000..9866cd0e992a --- /dev/null +++ b/core/java/android/content/UriRelativeFilter.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.Flags; +import android.net.Uri; +import android.os.Parcel; +import android.os.PatternMatcher; +import android.util.proto.ProtoOutputStream; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A filter for matching Intent URI Data as part of a + * {@link UriRelativeFilterGroup}. A single filter can only be + * matched against either a URI path, query or fragment + */ +@FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) +public final class UriRelativeFilter { + private static final String FILTER_STR = "filter"; + private static final String PART_STR = "part"; + private static final String PATTERN_STR = "pattern"; + static final String URI_RELATIVE_FILTER_STR = "uriRelativeFilter"; + + /** + * Value to indicate that the filter is to be applied to a URI path. + */ + public static final int PATH = 0; + /** + * Value to indicate that the filter is to be applied to a URI query. + */ + public static final int QUERY = 1; + /** + * Value to indicate that the filter is to be applied to a URI fragment. + */ + public static final int FRAGMENT = 2; + + /** @hide */ + @IntDef(value = { + PATH, + QUERY, + FRAGMENT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UriPart {} + + private final @UriPart int mUriPart; + private final @PatternMatcher.PatternType int mPatternType; + private final String mFilter; + + /** + * Creates a new UriRelativeFilter. + * + * @param uriPart The URI part this filter operates on. Can be either a + * {@link UriRelativeFilter#PATH}, {@link UriRelativeFilter#QUERY}, + * or {@link UriRelativeFilter#FRAGMENT}. + * @param patternType The pattern type of the filter. Can be either a + * {@link PatternMatcher#PATTERN_LITERAL}, + * {@link PatternMatcher#PATTERN_PREFIX}, +* {@link PatternMatcher#PATTERN_SUFFIX}, + * {@link PatternMatcher#PATTERN_SIMPLE_GLOB}, + * or {@link PatternMatcher#PATTERN_ADVANCED_GLOB}. + * @param filter A literal or pattern string depedning on patterType + * used to match a uriPart . + */ + public UriRelativeFilter( + @UriPart int uriPart, + @PatternMatcher.PatternType int patternType, + @NonNull String filter) { + mUriPart = uriPart; + com.android.internal.util.AnnotationValidations.validate( + UriPart.class, null, mUriPart); + mPatternType = patternType; + com.android.internal.util.AnnotationValidations.validate( + PatternMatcher.PatternType.class, null, mPatternType); + mFilter = filter; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mFilter); + } + + /** + * The URI part this filter operates on. + */ + public @UriPart int getUriPart() { + return mUriPart; + } + + /** + * The pattern type of the filter. + */ + public @PatternMatcher.PatternType int getPatternType() { + return mPatternType; + } + + /** + * The string used to filter the URI. + */ + public @NonNull String getFilter() { + return mFilter; + } + + /** + * Match this URI filter against an Intent's data. QUERY filters can + * match against any key value pair in the query string. PATH and + * FRAGMENT filters must match the entire string. + * + * @param data The full data string to match against, as supplied in + * Intent.data. + * + * @return true if there is a match. + */ + public boolean matchData(@NonNull Uri data) { + PatternMatcher pe = new PatternMatcher(mFilter, mPatternType); + switch (getUriPart()) { + case PATH: + return pe.match(data.getPath()); + case QUERY: + return matchQuery(pe, data.getQuery()); + case FRAGMENT: + return pe.match(data.getFragment()); + default: + return false; + } + } + + private boolean matchQuery(PatternMatcher pe, String query) { + if (query != null) { + String[] params = query.split("&"); + if (params.length == 1) { + params = query.split(";"); + } + for (int i = 0; i < params.length; i++) { + if (pe.match(params[i])) return true; + } + } + return false; + } + + /** @hide */ + public void dumpDebug(ProtoOutputStream proto, long fieldId) { + long token = proto.start(fieldId); + proto.write(UriRelativeFilterProto.URI_PART, mUriPart); + proto.write(UriRelativeFilterProto.PATTERN_TYPE, mPatternType); + proto.write(UriRelativeFilterProto.FILTER, mFilter); + proto.end(token); + } + + /** @hide */ + public void writeToXml(XmlSerializer serializer) throws IOException { + serializer.startTag(null, URI_RELATIVE_FILTER_STR); + serializer.attribute(null, PATTERN_STR, Integer.toString(mPatternType)); + serializer.attribute(null, PART_STR, Integer.toString(mUriPart)); + serializer.attribute(null, FILTER_STR, mFilter); + serializer.endTag(null, URI_RELATIVE_FILTER_STR); + } + + private String uriPartToString() { + switch (mUriPart) { + case PATH: + return "PATH"; + case QUERY: + return "QUERY"; + case FRAGMENT: + return "FRAGMENT"; + default: + return "UNKNOWN"; + } + } + + private String patternTypeToString() { + switch (mPatternType) { + case PatternMatcher.PATTERN_LITERAL: + return "LITERAL"; + case PatternMatcher.PATTERN_PREFIX: + return "PREFIX"; + case PatternMatcher.PATTERN_SIMPLE_GLOB: + return "GLOB"; + case PatternMatcher.PATTERN_ADVANCED_GLOB: + return "ADVANCED_GLOB"; + case PatternMatcher.PATTERN_SUFFIX: + return "SUFFIX"; + default: + return "UNKNOWN"; + } + } + + @Override + public String toString() { + return "UriRelativeFilter { " + + "uriPart = " + uriPartToString() + ", " + + "patternType = " + patternTypeToString() + ", " + + "filter = " + mFilter + + " }"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + UriRelativeFilter that = (UriRelativeFilter) o; + return mUriPart == that.mUriPart + && mPatternType == that.mPatternType + && java.util.Objects.equals(mFilter, that.mFilter); + } + + @Override + public int hashCode() { + int _hash = 1; + _hash = 31 * _hash + mUriPart; + _hash = 31 * _hash + mPatternType; + _hash = 31 * _hash + java.util.Objects.hashCode(mFilter); + return _hash; + } + + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mUriPart); + dest.writeInt(mPatternType); + dest.writeString(mFilter); + } + + /** @hide */ + UriRelativeFilter(@NonNull android.os.Parcel in) { + mUriPart = in.readInt(); + mPatternType = in.readInt(); + mFilter = in.readString(); + } + + /** @hide */ + public UriRelativeFilter(XmlPullParser parser) throws XmlPullParserException, IOException { + mUriPart = Integer.parseInt(parser.getAttributeValue(null, PART_STR)); + mPatternType = Integer.parseInt(parser.getAttributeValue(null, PATTERN_STR)); + mFilter = parser.getAttributeValue(null, FILTER_STR); + } +} diff --git a/core/java/android/content/UriRelativeFilterGroup.java b/core/java/android/content/UriRelativeFilterGroup.java new file mode 100644 index 000000000000..72c396a73ec8 --- /dev/null +++ b/core/java/android/content/UriRelativeFilterGroup.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.pm.Flags; +import android.net.Uri; +import android.os.Parcel; +import android.util.ArraySet; +import android.util.Log; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.util.CollectionUtils; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; + +/** + * An intent data matching group based on a URI's relative reference which + * includes the path, query and fragment. The group is only considered as + * matching if <em>all</em> UriRelativeFilters in the group match. Each + * UriRelativeFilter defines a matching rule for a URI path, query or fragment. + * A group must contain one or more UriRelativeFilters to match but does not need to + * contain UriRelativeFilters for all existing parts of a URI to match. + * + * <p>For example, given a URI that contains path, query and fragment parts, + * a group containing only a path filter will match the URI if the path + * filter matches the URI path. If the group contains a path and query + * filter, then the group will only match if both path and query filters + * match. If a URI contains only a path with no query or fragment then a + * group can only match if it contains only a matching path filter. If the + * group also contained additional query or fragment filters then it will + * not match.</p> + */ +@FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) +public final class UriRelativeFilterGroup { + private static final String ALLOW_STR = "allow"; + private static final String URI_RELATIVE_FILTER_GROUP_STR = "uriRelativeFilterGroup"; + + /** + * Value to indicate that the group match is allowed. + */ + public static final int ACTION_ALLOW = 0; + /** + * Value to indicate that the group match is blocked. + */ + public static final int ACTION_BLOCK = 1; + + /** @hide */ + @IntDef(value = { + ACTION_ALLOW, + ACTION_BLOCK + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Action {} + + private final @Action int mAction; + private final ArraySet<UriRelativeFilter> mUriRelativeFilters = new ArraySet<>(); + + /** + * New UriRelativeFilterGroup that matches a Intent data. + * + * @param action Whether this matching group should be allowed or disallowed. + */ + public UriRelativeFilterGroup(@Action int action) { + mAction = action; + } + + /** @hide */ + public UriRelativeFilterGroup(XmlPullParser parser) throws XmlPullParserException, IOException { + mAction = Integer.parseInt(parser.getAttributeValue(null, ALLOW_STR)); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals(UriRelativeFilter.URI_RELATIVE_FILTER_STR)) { + addUriRelativeFilter(new UriRelativeFilter(parser)); + } else { + Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName); + } + XmlUtils.skipCurrentTag(parser); + } + } + + /** + * Return {@link UriRelativeFilterGroup#ACTION_ALLOW} if a URI is allowed when matched + * and {@link UriRelativeFilterGroup#ACTION_BLOCK} if a URI is blacked when matched. + */ + public @Action int getAction() { + return mAction; + } + + /** + * Add a filter to the group. + */ + public void addUriRelativeFilter(@NonNull UriRelativeFilter uriRelativeFilter) { + Objects.requireNonNull(uriRelativeFilter); + if (!CollectionUtils.contains(mUriRelativeFilters, uriRelativeFilter)) { + mUriRelativeFilters.add(uriRelativeFilter); + } + } + + /** + * Returns a unmodifiable view of the UriRelativeFilters list in this group. + */ + @NonNull + public Collection<UriRelativeFilter> getUriRelativeFilters() { + return Collections.unmodifiableCollection(mUriRelativeFilters); + } + + /** + * Match all URI filter in this group against {@link Intent#getData()}. + * + * @param data The full data string to match against, as supplied in + * Intent.data. + * @return true if all filters match. + */ + public boolean matchData(@NonNull Uri data) { + if (mUriRelativeFilters.size() == 0) { + return false; + } + for (UriRelativeFilter filter : mUriRelativeFilters) { + if (!filter.matchData(data)) { + return false; + } + } + return true; + } + + /** @hide */ + public void dumpDebug(ProtoOutputStream proto, long fieldId) { + long token = proto.start(fieldId); + proto.write(UriRelativeFilterGroupProto.ACTION, mAction); + Iterator<UriRelativeFilter> it = mUriRelativeFilters.iterator(); + while (it.hasNext()) { + it.next().dumpDebug(proto, UriRelativeFilterGroupProto.URI_RELATIVE_FILTERS); + } + proto.end(token); + } + + /** @hide */ + public void writeToXml(XmlSerializer serializer) throws IOException { + serializer.startTag(null, URI_RELATIVE_FILTER_GROUP_STR); + serializer.attribute(null, ALLOW_STR, Integer.toString(mAction)); + Iterator<UriRelativeFilter> it = mUriRelativeFilters.iterator(); + while (it.hasNext()) { + UriRelativeFilter filter = it.next(); + filter.writeToXml(serializer); + } + serializer.endTag(null, URI_RELATIVE_FILTER_GROUP_STR); + } + + @Override + public String toString() { + return "UriRelativeFilterGroup { allow = " + mAction + + ", uri_filters = " + mUriRelativeFilters + ", }"; + } + + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mAction); + final int n = mUriRelativeFilters.size(); + if (n > 0) { + dest.writeInt(n); + Iterator<UriRelativeFilter> it = mUriRelativeFilters.iterator(); + while (it.hasNext()) { + it.next().writeToParcel(dest, flags); + } + } else { + dest.writeInt(0); + } + } + + /** @hide */ + UriRelativeFilterGroup(@NonNull Parcel src) { + mAction = src.readInt(); + final int n = src.readInt(); + for (int i = 0; i < n; i++) { + mUriRelativeFilters.add(new UriRelativeFilter(src)); + } + } +} diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index a97de6368b8c..62db65f15df3 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -128,4 +128,6 @@ interface ILauncherApps { /** Unregister a callback, so that it won't be called when LauncherApps dumps. */ void unRegisterDumpCallback(IDumpCallback cb); + + void setArchiveCompatibilityOptions(boolean enableIconOverlay, boolean enableUnarchivalConfirmation); } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 6dc8d4738c87..380de965b143 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -840,7 +840,7 @@ interface IPackageManager { ArchivedPackageParcel getArchivedPackage(in String packageName, int userId); - Bitmap getArchivedAppIcon(String packageName, in UserHandle user); + Bitmap getArchivedAppIcon(String packageName, in UserHandle user, String callingPackageName); boolean isAppArchivable(String packageName, in UserHandle user); } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 1d2b1aff46bc..50be983ec938 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -1801,6 +1801,31 @@ public class LauncherApps { } } + /** + * Enable or disable different archive compatibility options of the launcher. + * + * @param enableIconOverlay Provides a cloud overlay for archived apps to ensure users are aware + * that a certain app is archived. True by default. + * Launchers might want to disable this operation if they want to provide custom user experience + * to differentiate archived apps. + * @param enableUnarchivalConfirmation If true, the user is shown a confirmation dialog when + * they click an archived app, which explains that the app will be downloaded and restored in + * the background. True by default. + * Launchers might want to disable this operation if they provide sufficient, alternative user + * guidance to highlight that an unarchival is starting and ongoing once an archived app is + * tapped. E.g., this could be achieved by showing the unarchival progress around the icon. + */ + @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING) + public void setArchiveCompatibilityOptions(boolean enableIconOverlay, + boolean enableUnarchivalConfirmation) { + try { + mService.setArchiveCompatibilityOptions(enableIconOverlay, + enableUnarchivalConfirmation); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + /** @return position in mCallbacks for callback or -1 if not present. */ private int findCallbackLocked(Callback callback) { if (callback == null) { diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java index 269c6c27821c..1d0e2db78bcb 100644 --- a/core/java/android/content/pm/UserProperties.java +++ b/core/java/android/content/pm/UserProperties.java @@ -16,6 +16,9 @@ package android.content.pm; +import static android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -73,7 +76,7 @@ public final class UserProperties implements Parcelable { private static final String ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = "crossProfileContentSharingStrategy"; - + private static final String ATTR_PROFILE_API_VISIBILITY = "profileApiVisibility"; /** Index values of each property (to indicate whether they are present in this object). */ @IntDef(prefix = "INDEX_", value = { INDEX_SHOW_IN_LAUNCHER, @@ -93,6 +96,7 @@ public final class UserProperties implements Parcelable { INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE, INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY, INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING, + INDEX_PROFILE_API_VISIBILITY }) @Retention(RetentionPolicy.SOURCE) private @interface PropertyIndex { @@ -114,6 +118,7 @@ public final class UserProperties implements Parcelable { private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14; private static final int INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = 15; private static final int INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING = 16; + private static final int INDEX_PROFILE_API_VISIBILITY = 17; /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */ private long mPropertiesPresent = 0; @@ -450,6 +455,41 @@ public final class UserProperties implements Parcelable { @SuppressLint("UnflaggedApi") // b/306636213 public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; + /** + * Possible values for the profile visibility in public API surfaces. This indicates whether or + * not the information linked to the profile (userId, package names) should not be returned in + * API surfaces if a user is marked as hidden. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "PROFILE_API_VISIBILITY_", + value = { + PROFILE_API_VISIBILITY_UNKNOWN, + PROFILE_API_VISIBILITY_VISIBLE, + PROFILE_API_VISIBILITY_HIDDEN, + } + ) + public @interface ProfileApiVisibility { + } + /* + * The api visibility value for this profile user is undefined or unknown. + */ + @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES) + public static final int PROFILE_API_VISIBILITY_UNKNOWN = -1; + + /** + * Indicates that information about this profile user should be shown in API surfaces. + */ + @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES) + public static final int PROFILE_API_VISIBILITY_VISIBLE = 0; + + /** + * Indicates that information about this profile should be not be visible in API surfaces. + */ + @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES) + public static final int PROFILE_API_VISIBILITY_HIDDEN = 1; + /** * Creates a UserProperties (intended for the SystemServer) that stores a reference to the given @@ -510,6 +550,9 @@ public final class UserProperties implements Parcelable { setShowInQuietMode(orig.getShowInQuietMode()); setShowInSharingSurfaces(orig.getShowInSharingSurfaces()); setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy()); + if (android.multiuser.Flags.supportHidingProfiles()) { + setProfileApiVisibility(orig.getProfileApiVisibility()); + } } /** @@ -951,9 +994,31 @@ public final class UserProperties implements Parcelable { } private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy; + /** + * Returns the visibility of the profile user in API surfaces. Any information linked to the + * profile (userId, package names) should be hidden API surfaces if a user is marked as hidden. + */ + @NonNull + @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES) + public @ProfileApiVisibility int getProfileApiVisibility() { + if (isPresent(INDEX_PROFILE_API_VISIBILITY)) return mProfileApiVisibility; + if (mDefaultProperties != null) return mDefaultProperties.mProfileApiVisibility; + throw new SecurityException("You don't have permission to query profileApiVisibility"); + } + /** @hide */ + @NonNull + @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES) + public void setProfileApiVisibility(@ProfileApiVisibility int profileApiVisibility) { + this.mProfileApiVisibility = profileApiVisibility; + setPresent(INDEX_PROFILE_API_VISIBILITY); + } + private @ProfileApiVisibility int mProfileApiVisibility; @Override public String toString() { + String profileApiVisibility = + android.multiuser.Flags.supportHidingProfiles() ? ", mProfileApiVisibility=" + + getProfileApiVisibility() : ""; // Please print in increasing order of PropertyIndex. return "UserProperties{" + "mPropertiesPresent=" + Long.toBinaryString(mPropertiesPresent) @@ -977,6 +1042,7 @@ public final class UserProperties implements Parcelable { + ", mDeleteAppWithParent=" + getDeleteAppWithParent() + ", mAlwaysVisible=" + getAlwaysVisible() + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy() + + profileApiVisibility + "}"; } @@ -1010,6 +1076,9 @@ public final class UserProperties implements Parcelable { pw.println(prefix + " mAlwaysVisible=" + getAlwaysVisible()); pw.println(prefix + " mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy()); + if (android.multiuser.Flags.supportHidingProfiles()) { + pw.println(prefix + " mProfileApiVisibility=" + getProfileApiVisibility()); + } } /** @@ -1093,6 +1162,12 @@ public final class UserProperties implements Parcelable { break; case ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY: setCrossProfileContentSharingStrategy(parser.getAttributeInt(i)); + break; + case ATTR_PROFILE_API_VISIBILITY: + if (android.multiuser.Flags.supportHidingProfiles()) { + setProfileApiVisibility(parser.getAttributeInt(i)); + } + break; default: Slog.w(LOG_TAG, "Skipping unknown property " + attributeName); } @@ -1175,6 +1250,12 @@ public final class UserProperties implements Parcelable { serializer.attributeInt(null, ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY, mCrossProfileContentSharingStrategy); } + if (isPresent(INDEX_PROFILE_API_VISIBILITY)) { + if (android.multiuser.Flags.supportHidingProfiles()) { + serializer.attributeInt(null, ATTR_PROFILE_API_VISIBILITY, + mProfileApiVisibility); + } + } } // For use only with an object that has already had any permission-lacking fields stripped out. @@ -1198,6 +1279,7 @@ public final class UserProperties implements Parcelable { dest.writeBoolean(mDeleteAppWithParent); dest.writeBoolean(mAlwaysVisible); dest.writeInt(mCrossProfileContentSharingStrategy); + dest.writeInt(mProfileApiVisibility); } /** @@ -1225,6 +1307,7 @@ public final class UserProperties implements Parcelable { mDeleteAppWithParent = source.readBoolean(); mAlwaysVisible = source.readBoolean(); mCrossProfileContentSharingStrategy = source.readInt(); + mProfileApiVisibility = source.readInt(); } @Override @@ -1274,6 +1357,7 @@ public final class UserProperties implements Parcelable { private boolean mAlwaysVisible = false; private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy = CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION; + private @ProfileApiVisibility int mProfileApiVisibility = 0; /** * @hide @@ -1428,6 +1512,17 @@ public final class UserProperties implements Parcelable { return this; } + /** + * Sets the value for {@link #mProfileApiVisibility} + * @hide + */ + @NonNull + @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES) + public Builder setProfileApiVisibility(@ProfileApiVisibility int profileApiVisibility){ + mProfileApiVisibility = profileApiVisibility; + return this; + } + /** Builds a UserProperties object with *all* values populated. * @hide */ @@ -1452,7 +1547,8 @@ public final class UserProperties implements Parcelable { mAllowStoppingUserWithDelayedLocking, mDeleteAppWithParent, mAlwaysVisible, - mCrossProfileContentSharingStrategy); + mCrossProfileContentSharingStrategy, + mProfileApiVisibility); } } // end Builder @@ -1473,7 +1569,8 @@ public final class UserProperties implements Parcelable { boolean allowStoppingUserWithDelayedLocking, boolean deleteAppWithParent, boolean alwaysVisible, - @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy) { + @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy, + @ProfileApiVisibility int profileApiVisibility) { mDefaultProperties = null; setShowInLauncher(showInLauncher); setStartWithParent(startWithParent); @@ -1493,5 +1590,8 @@ public final class UserProperties implements Parcelable { setDeleteAppWithParent(deleteAppWithParent); setAlwaysVisible(alwaysVisible); setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy); + if (android.multiuser.Flags.supportHidingProfiles()) { + setProfileApiVisibility(profileApiVisibility); + } } } diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index fd872906f53b..caff4576c9c3 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -110,6 +110,14 @@ flag { } flag { + name: "relative_reference_intent_filters" + namespace: "package_manager_service" + description: "Feature flag to enable relative reference intent filters" + bug: "307556883" + is_fixed_read_only: true +} + +flag { name: "fix_duplicated_flags" namespace: "package_manager_service" description: "Feature flag to fix duplicated PackageManager flag values" @@ -168,3 +176,10 @@ flag { description: "Feature flag to allow the sandbox SDK to query intent activities of the client app." bug: "295842134" } + +flag { + name: "emergency_install_permission" + namespace: "permissions" + description: "Feature flag to enable permission EMERGENCY_INSTALL_PACKAGES" + bug: "321080601" +} diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index c08343713abb..efb8607f75f7 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -100,3 +100,11 @@ flag { description: "Enable only the API changes to support private space" bug: "299069460" } + +flag { + name: "support_hiding_profiles" + namespace: "profile_experiences" + description: "Allow the use of a hide_profile property to hide some profiles behind a permission" + bug: "316362775" + is_fixed_read_only: true +} diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java index 89f4985461b7..6ff96f42e433 100644 --- a/core/java/android/content/res/Element.java +++ b/core/java/android/content/res/Element.java @@ -93,6 +93,7 @@ public class Element { protected static final String TAG_SUPPORTS_GL_TEXTURE = "supports-gl-texture"; protected static final String TAG_SUPPORTS_INPUT = "supports-input"; protected static final String TAG_SUPPORTS_SCREENS = "supports-screens"; + protected static final String TAG_URI_RELATIVE_FILTER_GROUP = "uri-relative-filter-group"; protected static final String TAG_USES_CONFIGURATION = "uses-configuration"; protected static final String TAG_USES_FEATURE = "uses-feature"; protected static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; @@ -106,6 +107,11 @@ public class Element { protected static final String TAG_ATTR_BACKUP_AGENT = "backupAgent"; protected static final String TAG_ATTR_CATEGORY = "category"; + protected static final String TAG_ATTR_FRAGMENT = "fragment"; + protected static final String TAG_ATTR_FRAGMENT_ADVANCED_PATTERN = "fragmentAdvancedPattern"; + protected static final String TAG_ATTR_FRAGMENT_PATTERN = "fragmentPattern"; + protected static final String TAG_ATTR_FRAGMENT_PREFIX = "fragmentPrefix"; + protected static final String TAG_ATTR_FRAGMENT_SUFFIX = "fragmentSuffix"; protected static final String TAG_ATTR_HOST = "host"; protected static final String TAG_ATTR_MANAGE_SPACE_ACTIVITY = "manageSpaceActivity"; protected static final String TAG_ATTR_MIMETYPE = "mimeType"; @@ -122,6 +128,11 @@ public class Element { protected static final String TAG_ATTR_PERMISSION_GROUP = "permissionGroup"; protected static final String TAG_ATTR_PORT = "port"; protected static final String TAG_ATTR_PROCESS = "process"; + protected static final String TAG_ATTR_QUERY = "query"; + protected static final String TAG_ATTR_QUERY_ADVANCED_PATTERN = "queryAdvancedPattern"; + protected static final String TAG_ATTR_QUERY_PATTERN = "queryPattern"; + protected static final String TAG_ATTR_QUERY_PREFIX = "queryPrefix"; + protected static final String TAG_ATTR_QUERY_SUFFIX = "querySuffix"; protected static final String TAG_ATTR_READ_PERMISSION = "readPermission"; protected static final String TAG_ATTR_REQUIRED_ACCOUNT_TYPE = "requiredAccountType"; protected static final String TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_NAME = @@ -143,7 +154,7 @@ public class Element { // The length of mTagCounters corresponds to the number of tags defined in getCounterIdx. If new // tags are added then the size here should be increased to match. - private final TagCounter[] mTagCounters = new TagCounter[34]; + private final TagCounter[] mTagCounters = new TagCounter[35]; String mTag; @@ -238,9 +249,11 @@ public class Element { return 31; case TAG_INTENT: return 32; + case TAG_URI_RELATIVE_FILTER_GROUP: + return 33; default: // The size of the mTagCounters array should be equal to this value+1 - return 33; + return 34; } } @@ -276,6 +289,7 @@ public class Element { case TAG_SERVICE: case TAG_SUPPORTS_GL_TEXTURE: case TAG_SUPPORTS_SCREENS: + case TAG_URI_RELATIVE_FILTER_GROUP: case TAG_USES_CONFIGURATION: case TAG_USES_FEATURE: case TAG_USES_LIBRARY: @@ -322,6 +336,7 @@ public class Element { break; case TAG_INTENT: case TAG_INTENT_FILTER: + initializeCounter(TAG_URI_RELATIVE_FILTER_GROUP, 100); initializeCounter(TAG_ACTION, 20000); initializeCounter(TAG_CATEGORY, 40000); initializeCounter(TAG_DATA, 40000); @@ -354,6 +369,9 @@ public class Element { initializeCounter(TAG_INTENT, 2000); initializeCounter(TAG_PROVIDER, 8000); break; + case TAG_URI_RELATIVE_FILTER_GROUP: + initializeCounter(TAG_DATA, 100); + break; } } @@ -391,11 +409,21 @@ public class Element { case TAG_ATTR_VERSION_NAME: case TAG_ATTR_ZYGOTE_PRELOAD_NAME: return MAX_ATTR_LEN_NAME; + case TAG_ATTR_FRAGMENT: + case TAG_ATTR_FRAGMENT_ADVANCED_PATTERN: + case TAG_ATTR_FRAGMENT_PATTERN: + case TAG_ATTR_FRAGMENT_PREFIX: + case TAG_ATTR_FRAGMENT_SUFFIX: case TAG_ATTR_PATH: case TAG_ATTR_PATH_ADVANCED_PATTERN: case TAG_ATTR_PATH_PATTERN: case TAG_ATTR_PATH_PREFIX: case TAG_ATTR_PATH_SUFFIX: + case TAG_ATTR_QUERY: + case TAG_ATTR_QUERY_ADVANCED_PATTERN: + case TAG_ATTR_QUERY_PATTERN: + case TAG_ATTR_QUERY_PREFIX: + case TAG_ATTR_QUERY_SUFFIX: return MAX_ATTR_LEN_PATH; case TAG_ATTR_VALUE: return MAX_ATTR_LEN_VALUE; @@ -535,6 +563,16 @@ public class Element { case R.styleable.AndroidManifestData_pathPrefix: case R.styleable.AndroidManifestData_pathSuffix: case R.styleable.AndroidManifestData_pathAdvancedPattern: + case R.styleable.AndroidManifestData_query: + case R.styleable.AndroidManifestData_queryPattern: + case R.styleable.AndroidManifestData_queryPrefix: + case R.styleable.AndroidManifestData_querySuffix: + case R.styleable.AndroidManifestData_queryAdvancedPattern: + case R.styleable.AndroidManifestData_fragment: + case R.styleable.AndroidManifestData_fragmentPattern: + case R.styleable.AndroidManifestData_fragmentPrefix: + case R.styleable.AndroidManifestData_fragmentSuffix: + case R.styleable.AndroidManifestData_fragmentAdvancedPattern: return MAX_ATTR_LEN_PATH; default: return DEFAULT_MAX_STRING_ATTR_LENGTH; diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java index 530feadae836..73361ad25964 100644 --- a/core/java/android/credentials/GetCandidateCredentialsResponse.java +++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java @@ -19,7 +19,7 @@ package android.credentials; import android.annotation.Hide; import android.annotation.NonNull; import android.app.PendingIntent; -import android.credentials.ui.GetCredentialProviderData; +import android.credentials.selection.GetCredentialProviderData; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/credentials/ui/AuthenticationEntry.java b/core/java/android/credentials/selection/AuthenticationEntry.java index 9bd0871b3d3b..54589e1aed8b 100644 --- a/core/java/android/credentials/ui/AuthenticationEntry.java +++ b/core/java/android/credentials/selection/AuthenticationEntry.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -41,6 +44,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class AuthenticationEntry implements Parcelable { @NonNull private final String mKey; diff --git a/core/java/android/credentials/ui/BaseDialogResult.java b/core/java/android/credentials/selection/BaseDialogResult.java index e985a4666d31..d4a73c3c1175 100644 --- a/core/java/android/credentials/ui/BaseDialogResult.java +++ b/core/java/android/credentials/selection/BaseDialogResult.java @@ -14,11 +14,16 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.TestApi; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; @@ -35,6 +40,10 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) +@SuppressLint("ParcelNotFinal") // Test API only. This is never intended to be officially exposed. +// Instead proper final wrapper classes are defined (e.g. {@code FailureDialogResult}). public class BaseDialogResult implements Parcelable { /** Parses and returns a BaseDialogResult from the given resultData. */ @Nullable @@ -54,7 +63,8 @@ public class BaseDialogResult implements Parcelable { * The intent extra key for the {@code BaseDialogResult} object when the credential * selector activity finishes. */ - private static final String EXTRA_BASE_RESULT = "android.credentials.ui.extra.BASE_RESULT"; + private static final String EXTRA_BASE_RESULT = + "android.credentials.selection.extra.BASE_RESULT"; /** @hide **/ @IntDef(prefix = {"RESULT_CODE_"}, value = { @@ -92,13 +102,19 @@ public class BaseDialogResult implements Parcelable { mRequestToken = requestToken; } - /** Returns the unique identifier for the request that launched the operation. */ + /** + * Returns the unique identifier for the request that launched the operation. + * + * @deprecated do not use + */ @Nullable @Deprecated public IBinder getRequestToken() { return mRequestToken; } + @SuppressLint("ParcelConstructor") // Test API only. This is never intended to be officially + // exposed. Instead proper final wrapper classes are defined (e.g. {@code FailureDialogResult}). protected BaseDialogResult(@NonNull Parcel in) { IBinder requestToken = in.readStrongBinder(); mRequestToken = requestToken; diff --git a/core/java/android/credentials/ui/CancelUiRequest.java b/core/java/android/credentials/selection/CancelUiRequest.java index 712424ced41b..fca0e2ad184e 100644 --- a/core/java/android/credentials/ui/CancelUiRequest.java +++ b/core/java/android/credentials/selection/CancelUiRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; import android.annotation.NonNull; import android.os.IBinder; @@ -38,7 +38,7 @@ public final class CancelUiRequest implements Parcelable { */ @NonNull public static final String EXTRA_CANCEL_UI_REQUEST = - "android.credentials.ui.extra.CANCEL_UI_REQUEST"; + "android.credentials.selection.extra.CANCEL_UI_REQUEST"; @NonNull private final IBinder mToken; diff --git a/core/java/android/credentials/ui/Constants.java b/core/java/android/credentials/selection/Constants.java index 68f28e74dad0..7e6c7810f1ab 100644 --- a/core/java/android/credentials/ui/Constants.java +++ b/core/java/android/credentials/selection/Constants.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; /** * Constants for the ui protocol that doesn't fit into other individual data structures. @@ -27,14 +27,14 @@ public class Constants { * The intent extra key for the {@code ResultReceiver} object when launching the UX activities. */ public static final String EXTRA_RESULT_RECEIVER = - "android.credentials.ui.extra.RESULT_RECEIVER"; + "android.credentials.selection.extra.RESULT_RECEIVER"; /** * The intent extra key for indicating whether the bottom sheet should be started directly * on the 'All Options' screen. */ public static final String EXTRA_REQ_FOR_ALL_OPTIONS = - "android.credentials.ui.extra.REQ_FOR_ALL_OPTIONS"; + "android.credentials.selection.extra.REQ_FOR_ALL_OPTIONS"; private Constants() {} } diff --git a/core/java/android/credentials/ui/CreateCredentialProviderData.java b/core/java/android/credentials/selection/CreateCredentialProviderData.java index d7a4f5bdbfca..fc80ea8cec8a 100644 --- a/core/java/android/credentials/ui/CreateCredentialProviderData.java +++ b/core/java/android/credentials/selection/CreateCredentialProviderData.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -33,6 +36,7 @@ import java.util.List; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class CreateCredentialProviderData extends ProviderData implements Parcelable { @NonNull private final List<Entry> mSaveEntries; @@ -112,6 +116,7 @@ public final class CreateCredentialProviderData extends ProviderData implements * @hide */ @TestApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public static final class Builder { @NonNull private String mProviderFlattenedComponentName; @NonNull private List<Entry> mSaveEntries = new ArrayList<>(); diff --git a/core/java/android/credentials/ui/CreateCredentialProviderInfo.java b/core/java/android/credentials/selection/CreateCredentialProviderInfo.java index 41ca852c2351..78b9fd445f49 100644 --- a/core/java/android/credentials/ui/CreateCredentialProviderInfo.java +++ b/core/java/android/credentials/selection/CreateCredentialProviderInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/core/java/android/credentials/ui/DisabledProviderData.java b/core/java/android/credentials/selection/DisabledProviderData.java index 8bccdc9a199f..b6f6ad4d60d7 100644 --- a/core/java/android/credentials/ui/DisabledProviderData.java +++ b/core/java/android/credentials/selection/DisabledProviderData.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.TestApi; import android.os.Parcel; @@ -27,6 +30,7 @@ import android.os.Parcelable; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class DisabledProviderData extends ProviderData implements Parcelable { public DisabledProviderData( diff --git a/core/java/android/credentials/ui/DisabledProviderInfo.java b/core/java/android/credentials/selection/DisabledProviderInfo.java index 7ce63681cf1c..7d7dbc2dd689 100644 --- a/core/java/android/credentials/ui/DisabledProviderInfo.java +++ b/core/java/android/credentials/selection/DisabledProviderInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; import android.annotation.NonNull; diff --git a/core/java/android/credentials/ui/Entry.java b/core/java/android/credentials/selection/Entry.java index 84694471ce70..bcf4ee3fb819 100644 --- a/core/java/android/credentials/ui/Entry.java +++ b/core/java/android/credentials/selection/Entry.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -34,6 +37,7 @@ import com.android.internal.util.AnnotationValidations; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class Entry implements Parcelable { @NonNull private final String mKey; diff --git a/core/java/android/credentials/ui/FailureDialogResult.java b/core/java/android/credentials/selection/FailureDialogResult.java index abd5a92415d8..218aa469887e 100644 --- a/core/java/android/credentials/ui/FailureDialogResult.java +++ b/core/java/android/credentials/selection/FailureDialogResult.java @@ -14,10 +14,14 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; @@ -28,6 +32,8 @@ import android.os.Parcelable; * * @hide */ +@TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class FailureDialogResult extends BaseDialogResult implements Parcelable { /** Parses and returns a UserSelectionDialogResult from the given resultData. */ @Nullable @@ -50,7 +56,7 @@ public final class FailureDialogResult extends BaseDialogResult implements Parce * selector activity finishes. */ private static final String EXTRA_FAILURE_RESULT = - "android.credentials.ui.extra.FAILURE_RESULT"; + "android.credentials.selection.extra.FAILURE_RESULT"; @Nullable private final String mErrorMessage; @@ -66,7 +72,7 @@ public final class FailureDialogResult extends BaseDialogResult implements Parce return mErrorMessage; } - protected FailureDialogResult(@NonNull Parcel in) { + private FailureDialogResult(@NonNull Parcel in) { super(in); mErrorMessage = in.readString8(); } diff --git a/core/java/android/credentials/ui/FailureResult.java b/core/java/android/credentials/selection/FailureResult.java index ec584170fba2..93ba671fa14f 100644 --- a/core/java/android/credentials/ui/FailureResult.java +++ b/core/java/android/credentials/selection/FailureResult.java @@ -14,11 +14,15 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,7 +32,9 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ -public final class FailureResult implements UiResult { +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) +public final class FailureResult { @Nullable private final String mErrorMessage; @NonNull diff --git a/core/java/android/credentials/ui/GetCredentialProviderData.java b/core/java/android/credentials/selection/GetCredentialProviderData.java index 481419b4f732..2d09f6052db8 100644 --- a/core/java/android/credentials/ui/GetCredentialProviderData.java +++ b/core/java/android/credentials/selection/GetCredentialProviderData.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -33,6 +36,7 @@ import java.util.List; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class GetCredentialProviderData extends ProviderData implements Parcelable { @NonNull private final List<Entry> mCredentialEntries; @@ -141,6 +145,7 @@ public final class GetCredentialProviderData extends ProviderData implements Par * @hide */ @TestApi + @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public static final class Builder { @NonNull private String mProviderFlattenedComponentName; diff --git a/core/java/android/credentials/ui/GetCredentialProviderInfo.java b/core/java/android/credentials/selection/GetCredentialProviderInfo.java index bac71472acd1..db0fb84fa76c 100644 --- a/core/java/android/credentials/ui/GetCredentialProviderInfo.java +++ b/core/java/android/credentials/selection/GetCredentialProviderInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java index 5e1e0efe39c4..c3a09ae61754 100644 --- a/core/java/android/credentials/ui/IntentFactory.java +++ b/core/java/android/credentials/selection/IntentFactory.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.TestApi; @@ -34,6 +37,7 @@ import java.util.ArrayList; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public class IntentFactory { /** diff --git a/core/java/android/credentials/ui/IntentHelper.java b/core/java/android/credentials/selection/IntentHelper.java index c5f34c1440a7..6bcd05afc00f 100644 --- a/core/java/android/credentials/ui/IntentHelper.java +++ b/core/java/android/credentials/selection/IntentHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/core/java/android/credentials/ui/ProviderData.java b/core/java/android/credentials/selection/ProviderData.java index 1e5aa24d462d..e7a7d7744585 100644 --- a/core/java/android/credentials/ui/ProviderData.java +++ b/core/java/android/credentials/selection/ProviderData.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.TestApi; @@ -30,6 +33,7 @@ import com.android.internal.util.AnnotationValidations; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) @SuppressLint({"ParcelCreator", "ParcelNotFinal"}) public abstract class ProviderData implements Parcelable { @@ -38,13 +42,13 @@ public abstract class ProviderData implements Parcelable { * launching the UX activities. */ public static final String EXTRA_ENABLED_PROVIDER_DATA_LIST = - "android.credentials.ui.extra.ENABLED_PROVIDER_DATA_LIST"; + "android.credentials.selection.extra.ENABLED_PROVIDER_DATA_LIST"; /** * The intent extra key for the list of {@code ProviderData} from disabled providers when * launching the UX activities. */ public static final String EXTRA_DISABLED_PROVIDER_DATA_LIST = - "android.credentials.ui.extra.DISABLED_PROVIDER_DATA_LIST"; + "android.credentials.selection.extra.DISABLED_PROVIDER_DATA_LIST"; @NonNull private final String mProviderFlattenedComponentName; @@ -63,6 +67,9 @@ public abstract class ProviderData implements Parcelable { return mProviderFlattenedComponentName; } + @SuppressLint("ParcelConstructor") // Test API only. This is never intended to be officially + // exposed. Instead proper final wrapper classes are defined (e.g. + // {@code GetCredentialProviderInfo}). protected ProviderData(@NonNull Parcel in) { String providerFlattenedComponentName = in.readString8(); mProviderFlattenedComponentName = providerFlattenedComponentName; diff --git a/core/java/android/credentials/ui/ProviderPendingIntentResponse.java b/core/java/android/credentials/selection/ProviderPendingIntentResponse.java index 11cc21f9d2db..281f34a5d195 100644 --- a/core/java/android/credentials/ui/ProviderPendingIntentResponse.java +++ b/core/java/android/credentials/selection/ProviderPendingIntentResponse.java @@ -14,14 +14,19 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; +import android.os.ResultReceiver; /** * Result of launching a provider's PendingIntent associated with an {@link Entry} after it is @@ -33,12 +38,29 @@ import android.os.Parcelable; * * @hide */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class ProviderPendingIntentResponse implements Parcelable { private final int mResultCode; @Nullable private final Intent mResultData; - /** Constructs a {@link ProviderPendingIntentResponse}. */ + /** + * Constructs a {@link ProviderPendingIntentResponse}. + * + * When a user makes a selection, you should launch the associated provider PendingIntent, + * and expect the provider activity to complete and set + * {@link android.app.Activity#setResult(int, Intent)}. You should then immediately pass back + * the provider activity result code and data to the system service using this data class, + * via the {@link ResultHelper#sendUserSelectionResult(ResultReceiver, UserSelectionResult)} + * API. + * + * @param resultCode the resultCode returned from the provider activity + * @param resultData the result data returned from the provider activity; only set to null if + * the provider result (a provider would set it via + * {@link android.app.Activity#setResult(int, Intent)}) your UI received + * was actually null + */ public ProviderPendingIntentResponse(int resultCode, @Nullable Intent resultData) { mResultCode = resultCode; mResultData = resultData; @@ -73,14 +95,29 @@ public final class ProviderPendingIntentResponse implements Parcelable { dest.writeTypedObject(mResultData, flags); } - /** Returns the result code associated with this provider PendingIntent activity result. */ + /** + * Returns the result code associated with this provider PendingIntent activity result, i.e. + * the {@code resultCode} that the provider activity has set using the + * {@link android.app.Activity#setResult(int, Intent)} API. + */ public int getResultCode() { return mResultCode; } - /** Returns the result data associated with this provider PendingIntent activity result. */ + /** + * Returns the result data associated with this provider PendingIntent activity result, i.e. + * the {@code data} that the provider activity has set using the + * {@link android.app.Activity#setResult(int, Intent)} API. + * + * Notice that this value can be null if the provider UI result (a provider would set it via + * {@link android.app.Activity#setResult(int, Intent)}) that your UI received was actually null, + * which indicates an implementation error on the provider side. The system service will + * gracefully handle this by passing back an API exception ( + * {@link android.credentials.GetCredentialException} or + * {@link android.credentials.CreateCredentialException}). + */ @SuppressLint("IntentBuilderName") // Not building a new intent. - @NonNull + @Nullable public Intent getResultData() { return mResultData; } diff --git a/core/java/android/credentials/ui/RequestInfo.java b/core/java/android/credentials/selection/RequestInfo.java index f65158444e48..7d6ea7ee6b8c 100644 --- a/core/java/android/credentials/ui/RequestInfo.java +++ b/core/java/android/credentials/selection/RequestInfo.java @@ -14,8 +14,11 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; @@ -39,39 +42,46 @@ import java.util.List; * @hide */ @TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class RequestInfo implements Parcelable { /** * The intent extra key for the {@code RequestInfo} object when launching the UX * activities. */ - @NonNull public static final String EXTRA_REQUEST_INFO = - "android.credentials.ui.extra.REQUEST_INFO"; + @NonNull + public static final String EXTRA_REQUEST_INFO = + "android.credentials.selection.extra.REQUEST_INFO"; /** * Type value for any request that does not require UI. */ - @NonNull public static final String TYPE_UNDEFINED = "android.credentials.ui.TYPE_UNDEFINED"; + @NonNull + public static final String TYPE_UNDEFINED = "android.credentials.selection.TYPE_UNDEFINED"; /** * Type value for a getCredential request. */ - @NonNull public static final String TYPE_GET = "android.credentials.ui.TYPE_GET"; + @NonNull + public static final String TYPE_GET = "android.credentials.selection.TYPE_GET"; /** * Type value for a getCredential request that utilizes the credential registry. * * @hide */ - @NonNull public static final String TYPE_GET_VIA_REGISTRY = - "android.credentials.ui.TYPE_GET_VIA_REGISTRY"; + @NonNull + public static final String TYPE_GET_VIA_REGISTRY = + "android.credentials.selection.TYPE_GET_VIA_REGISTRY"; /** * Type value for a createCredential request. */ - @NonNull public static final String TYPE_CREATE = "android.credentials.ui.TYPE_CREATE"; + @NonNull + public static final String TYPE_CREATE = "android.credentials.selection.TYPE_CREATE"; /** @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef(value = { TYPE_GET, TYPE_CREATE }) - public @interface RequestType {} + @StringDef(value = {TYPE_GET, TYPE_CREATE}) + public @interface RequestType { + } @NonNull private final IBinder mToken; @@ -82,6 +92,9 @@ public final class RequestInfo implements Parcelable { @NonNull private final List<String> mDefaultProviderIds; + @NonNull + private final List<String> mRegistryProviderIds; + @Nullable private final GetCredentialRequest mGetCredentialRequest; @@ -105,11 +118,7 @@ public final class RequestInfo implements Parcelable { /*defaultProviderIds=*/ new ArrayList<>()); } - /** - * Creates new {@code RequestInfo} for a create-credential flow. - * - * @hide - */ + /** Creates new {@code RequestInfo} for a create-credential flow. */ @NonNull public static RequestInfo newCreateRequestInfo( @NonNull IBinder token, @NonNull CreateCredentialRequest createCredentialRequest, @@ -120,11 +129,7 @@ public final class RequestInfo implements Parcelable { hasPermissionToOverrideDefault, defaultProviderIds); } - /** - * Creates new {@code RequestInfo} for a get-credential flow. - * - * @hide - */ + /** Creates new {@code RequestInfo} for a get-credential flow. */ @NonNull public static RequestInfo newGetRequestInfo( @NonNull IBinder token, @NonNull GetCredentialRequest getCredentialRequest, @@ -147,11 +152,7 @@ public final class RequestInfo implements Parcelable { } - /** - * Returns whether the calling package has the permission - * - * @hide - */ + /** Returns whether the calling package has the permission. */ public boolean hasPermissionToOverrideDefault() { return mHasPermissionToOverrideDefault; } @@ -185,13 +186,11 @@ public final class RequestInfo implements Parcelable { } /** - * Returns default provider identifier (flattened component name) configured from the user + * Returns default provider identifiers (component or package name) configured from the user * settings. * * Will only be possibly non-empty for the create use case. Not meaningful for the sign-in use * case. - * - * @hide */ @NonNull public List<String> getDefaultProviderIds() { @@ -199,6 +198,15 @@ public final class RequestInfo implements Parcelable { } /** + * Returns provider identifiers (component or package name) that have been validated to provide + * registry entries. + */ + @NonNull + public List<String> getRegistryProviderIds() { + return mRegistryProviderIds; + } + + /** * Returns the non-null GetCredentialRequest when the type of the request is {@link * #TYPE_GET}, or null otherwise. */ @@ -220,6 +228,7 @@ public final class RequestInfo implements Parcelable { mGetCredentialRequest = getCredentialRequest; mHasPermissionToOverrideDefault = hasPermissionToOverrideDefault; mDefaultProviderIds = defaultProviderIds == null ? new ArrayList<>() : defaultProviderIds; + mRegistryProviderIds = new ArrayList<>(); } private RequestInfo(@NonNull Parcel in) { @@ -241,6 +250,7 @@ public final class RequestInfo implements Parcelable { mGetCredentialRequest = getCredentialRequest; mHasPermissionToOverrideDefault = in.readBoolean(); mDefaultProviderIds = in.createStringArrayList(); + mRegistryProviderIds = in.createStringArrayList(); } @Override @@ -252,6 +262,7 @@ public final class RequestInfo implements Parcelable { dest.writeTypedObject(mGetCredentialRequest, flags); dest.writeBoolean(mHasPermissionToOverrideDefault); dest.writeStringList(mDefaultProviderIds); + dest.writeStringList(mRegistryProviderIds); } @Override @@ -259,7 +270,8 @@ public final class RequestInfo implements Parcelable { return 0; } - @NonNull public static final Creator<RequestInfo> CREATOR = new Creator<>() { + @NonNull + public static final Creator<RequestInfo> CREATOR = new Creator<>() { @Override public RequestInfo createFromParcel(@NonNull Parcel in) { return new RequestInfo(in); diff --git a/core/java/android/credentials/ui/ResultHelper.java b/core/java/android/credentials/selection/ResultHelper.java index 7b9d5e87d666..d6347b0086ff 100644 --- a/core/java/android/credentials/ui/ResultHelper.java +++ b/core/java/android/credentials/selection/ResultHelper.java @@ -14,9 +14,13 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.content.Intent; import android.os.Bundle; import android.os.ResultReceiver; @@ -26,13 +30,16 @@ import android.os.ResultReceiver; * * @hide */ +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class ResultHelper { /** * Sends the {@code failureResult} that caused the UI to stop back to the CredentialManager * service. * - * The {code resultReceiver} for a UI flow can be extracted from the UI launch intent via - * {@link IntentHelper#extractResultReceiver(Intent)}. + * @param resultReceiver the ResultReceiver sent from the system service, that can be extracted + * from the launch intent via + * {@link IntentHelper#extractResultReceiver(Intent)} */ public static void sendFailureResult(@NonNull ResultReceiver resultReceiver, @NonNull FailureResult failureResult) { @@ -46,8 +53,9 @@ public final class ResultHelper { /** * Sends the completed {@code userSelectionResult} back to the CredentialManager service. * - * The {code resultReceiver} for a UI flow can be extracted from the UI launch intent via - * {@link IntentHelper#extractResultReceiver(Intent)}. + * @param resultReceiver the ResultReceiver sent from the system service, that can be extracted + * from the launch intent via + * {@link IntentHelper#extractResultReceiver(Intent)} */ public static void sendUserSelectionResult(@NonNull ResultReceiver resultReceiver, @NonNull UserSelectionResult userSelectionResult) { diff --git a/core/java/android/credentials/ui/UserSelectionDialogResult.java b/core/java/android/credentials/selection/UserSelectionDialogResult.java index 3089bf674b95..50d5aa3c51bd 100644 --- a/core/java/android/credentials/ui/UserSelectionDialogResult.java +++ b/core/java/android/credentials/selection/UserSelectionDialogResult.java @@ -14,10 +14,14 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; @@ -30,17 +34,19 @@ import com.android.internal.util.AnnotationValidations; * * @hide */ +@TestApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) public final class UserSelectionDialogResult extends BaseDialogResult implements Parcelable { /** Parses and returns a UserSelectionDialogResult from the given resultData. */ @Nullable public static UserSelectionDialogResult fromResultData(@NonNull Bundle resultData) { return resultData.getParcelable( - EXTRA_USER_SELECTION_RESULT, UserSelectionDialogResult.class); + EXTRA_USER_SELECTION_RESULT, UserSelectionDialogResult.class); } /** * Used for the UX to construct the {@code resultData Bundle} to send via the {@code - * ResultReceiver}. + * ResultReceiver}. */ public static void addToBundle( @NonNull UserSelectionDialogResult result, @NonNull Bundle bundle) { @@ -52,12 +58,16 @@ public final class UserSelectionDialogResult extends BaseDialogResult implements * selector activity finishes. */ private static final String EXTRA_USER_SELECTION_RESULT = - "android.credentials.ui.extra.USER_SELECTION_RESULT"; + "android.credentials.selection.extra.USER_SELECTION_RESULT"; - @NonNull private final String mProviderId; - @NonNull private final String mEntryKey; - @NonNull private final String mEntrySubkey; - @Nullable private ProviderPendingIntentResponse mProviderPendingIntentResponse; + @NonNull + private final String mProviderId; + @NonNull + private final String mEntryKey; + @NonNull + private final String mEntrySubkey; + @Nullable + private ProviderPendingIntentResponse mProviderPendingIntentResponse; public UserSelectionDialogResult( @Nullable IBinder requestToken, @NonNull String providerId, @@ -103,7 +113,7 @@ public final class UserSelectionDialogResult extends BaseDialogResult implements return mProviderPendingIntentResponse; } - protected UserSelectionDialogResult(@NonNull Parcel in) { + private UserSelectionDialogResult(@NonNull Parcel in) { super(in); String providerId = in.readString8(); String entryKey = in.readString8(); @@ -134,14 +144,14 @@ public final class UserSelectionDialogResult extends BaseDialogResult implements public static final @NonNull Creator<UserSelectionDialogResult> CREATOR = new Creator<UserSelectionDialogResult>() { - @Override - public UserSelectionDialogResult createFromParcel(@NonNull Parcel in) { - return new UserSelectionDialogResult(in); - } - - @Override - public UserSelectionDialogResult[] newArray(int size) { - return new UserSelectionDialogResult[size]; - } - }; + @Override + public UserSelectionDialogResult createFromParcel(@NonNull Parcel in) { + return new UserSelectionDialogResult(in); + } + + @Override + public UserSelectionDialogResult[] newArray(int size) { + return new UserSelectionDialogResult[size]; + } + }; } diff --git a/core/java/android/credentials/ui/UserSelectionResult.java b/core/java/android/credentials/selection/UserSelectionResult.java index 431dc631f3f6..235a5d5723ea 100644 --- a/core/java/android/credentials/ui/UserSelectionResult.java +++ b/core/java/android/credentials/selection/UserSelectionResult.java @@ -14,10 +14,14 @@ * limitations under the License. */ -package android.credentials.ui; +package android.credentials.selection; +import static android.credentials.flags.Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import com.android.internal.util.Preconditions; @@ -27,7 +31,9 @@ import com.android.internal.util.Preconditions; * * @hide */ -public final class UserSelectionResult implements UiResult { +@SystemApi +@FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) +public final class UserSelectionResult { @NonNull private final String mProviderId; @NonNull @@ -40,31 +46,46 @@ public final class UserSelectionResult implements UiResult { /** * Constructs a {@link UserSelectionResult}. * - * @throws IllegalArgumentException if {@code providerId} is empty + * @param providerId the provider identifier (component name or package name) whose entry was + * selected by the user; the value should map to the + * {@link GetCredentialProviderInfo#getProviderName()} that provided this entry + * @param entryKey the identifier of this selected entry, i.e. the selected entry's + * {@link Entry#getKey()} + * @param entrySubkey the sub-identifier of this selected entry, i.e. the selected entry's + * {@link Entry#getSubkey()} + * @param providerPendingIntentResponse the provider activity result of launching the provider + * PendingIntent associated with this selection; or null + * if the associated selection didn't have an associated + * provider PendingIntent + * @throws IllegalArgumentException if {@code providerId}, {@code entryKey}, or + * {@code entrySubkey} is empty */ public UserSelectionResult(@NonNull String providerId, @NonNull String entryKey, @NonNull String entrySubkey, @Nullable ProviderPendingIntentResponse providerPendingIntentResponse) { mProviderId = Preconditions.checkStringNotEmpty(providerId); - mEntryKey = Preconditions.checkNotNull(entryKey); - mEntrySubkey = Preconditions.checkNotNull(entrySubkey); + mEntryKey = Preconditions.checkStringNotEmpty(entryKey); + mEntrySubkey = Preconditions.checkStringNotEmpty(entrySubkey); mProviderPendingIntentResponse = providerPendingIntentResponse; } - /** Returns provider package name whose entry was selected by the user. */ + /** + * Returns the provider identifier (component name or package name) whose entry was selected by + * the user. + */ @NonNull public String getProviderId() { return mProviderId; } - /** Returns the key of the visual entry that the user selected. */ + /** Returns the identifier of the visual entry that the user selected. */ @NonNull public String getEntryKey() { return mEntryKey; } - /** Returns the subkey of the visual entry that the user selected. */ + /** Returns the sub-identifier of the visual entry that the user selected. */ @NonNull public String getEntrySubkey() { return mEntrySubkey; diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl index 7c54a9b01dde..509bcb8e3d23 100644 --- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl +++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl @@ -26,6 +26,7 @@ parcelable CameraOutputConfig Surface surface; int imageFormat; int capacity; + long usage; const int TYPE_SURFACE = 0; const int TYPE_IMAGEREADER = 1; diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index 98bc31161591..f6c8f36a1b01 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -1182,7 +1182,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes return null; } ImageReader reader = ImageReader.newInstance(output.size.width, - output.size.height, output.imageFormat, output.capacity); + output.size.height, output.imageFormat, output.capacity, + output.usage); mReaderMap.put(output.outputId.id, reader); return reader.getSurface(); case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER: diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java index db14c08b3698..da6b9c25e2ba 100644 --- a/core/java/android/hardware/radio/RadioMetadata.java +++ b/core/java/android/hardware/radio/RadioMetadata.java @@ -507,10 +507,16 @@ public final class RadioMetadata implements Parcelable { * * @param key The key the value is stored under. * @return a bitmap identifier or 0 if it's missing. - * @hide This API is not thoroughly elaborated yet + * @throws NullPointerException if metadata key is {@code null} + * @throws IllegalArgumentException if the metadata with the key is not found in + * metadata or the key is not of bitmap-key type */ + @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) public int getBitmapId(@NonNull String key) { - if (!METADATA_KEY_ICON.equals(key) && !METADATA_KEY_ART.equals(key)) return 0; + Objects.requireNonNull(key, "Metadata key can not be null"); + if (!METADATA_KEY_ICON.equals(key) && !METADATA_KEY_ART.equals(key)) { + throw new IllegalArgumentException("Failed to retrieve key " + key + " as bitmap key"); + } return getInt(key); } diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java index 9b2bcdea5f30..7c5c00369e93 100644 --- a/core/java/android/hardware/radio/RadioTuner.java +++ b/core/java/android/hardware/radio/RadioTuner.java @@ -17,6 +17,7 @@ package android.hardware.radio; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -332,29 +333,30 @@ public abstract class RadioTuner { public abstract int getProgramInformation(RadioManager.ProgramInfo[] info); /** - * Retrieves a {@link Bitmap} for the given image ID or null, + * Retrieves a {@link Bitmap} for the given image ID or throw {@link IllegalArgumentException}, * if the image was missing from the tuner. * * <p>This involves doing a call to the tuner, so the bitmap should be cached * on the application side. * - * <p>If the method returns null for non-zero ID, it means the image was - * updated on the tuner side. There is a race conditon between fetching - * image for an old ID and tuner updating the image (and cleaning up the + * <p>If the method throws {@link IllegalArgumentException} for non-zero ID, it + * means the image was updated on the tuner side. There is a race condition between + * fetching image for an old ID and tuner updating the image (and cleaning up the * old image). In such case, a new ProgramInfo with updated image id will * be sent with a {@link Callback#onProgramInfoChanged(RadioManager.ProgramInfo)} * callback. * * @param id The image identifier, retrieved with * {@link RadioMetadata#getBitmapId(String)}. - * @return A {@link Bitmap} or null. - * @throws IllegalArgumentException if id==0 - * @hide This API is not thoroughly elaborated yet + * @return A {@link Bitmap} for the given image ID. + * @throws IllegalArgumentException if id is 0 or the referenced image id no longer exists. */ - @SuppressWarnings("HiddenAbstractMethod") + @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) - public abstract @Nullable Bitmap getMetadataImage(int id); - + public @NonNull Bitmap getMetadataImage(int id) { + throw new UnsupportedOperationException( + "Getting metadata image must be implemented in child classes"); + } /** * Initiates a background scan to update internally cached program list. * diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java index ba31ca3627bf..63b2d4cdd1fb 100644 --- a/core/java/android/hardware/radio/TunerAdapter.java +++ b/core/java/android/hardware/radio/TunerAdapter.java @@ -16,6 +16,7 @@ package android.hardware.radio; +import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Bitmap; import android.os.RemoteException; @@ -251,10 +252,18 @@ final class TunerAdapter extends RadioTuner { } @Override - @Nullable + @NonNull public Bitmap getMetadataImage(int id) { + if (id == 0) { + throw new IllegalArgumentException("Invalid metadata image id 0"); + } try { - return mTuner.getImage(id); + Bitmap bitmap = mTuner.getImage(id); + if (bitmap == null) { + throw new IllegalArgumentException("Metadata image with id " + id + + " is not available"); + } + return bitmap; } catch (RemoteException e) { throw new RuntimeException("Service died", e); } diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 05b7827f586f..b7556dfb51af 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -292,7 +292,7 @@ public class Binder implements IBinder { sWarnOnBlockingOnCurrentThread.set(sWarnOnBlocking); } - private static ThreadLocal<SomeArgs> sIdentity$ravenwood; + private static volatile ThreadLocal<SomeArgs> sIdentity$ravenwood; @android.ravenwood.annotation.RavenwoodKeepWholeClass private static class IdentitySupplier implements Supplier<SomeArgs> { diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java index fcd57313a28d..36730cb07344 100644 --- a/core/java/android/os/HandlerThread.java +++ b/core/java/android/os/HandlerThread.java @@ -35,6 +35,7 @@ public class HandlerThread extends Thread { public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; + onCreated(); } /** @@ -46,8 +47,21 @@ public class HandlerThread extends Thread { public HandlerThread(String name, int priority) { super(name); mPriority = priority; + onCreated(); } - + + /** @hide */ + @android.ravenwood.annotation.RavenwoodReplace + protected void onCreated() { + } + + /** @hide */ + protected void onCreated$ravenwood() { + // Mark ourselves as daemon to enable tests to terminate quickly when finished, despite + // any HandlerThread instances that may be lingering around + setDaemon(true); + } + /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. diff --git a/core/java/android/os/PatternMatcher.java b/core/java/android/os/PatternMatcher.java index b5425b43fe7e..79a2c59d6a03 100644 --- a/core/java/android/os/PatternMatcher.java +++ b/core/java/android/os/PatternMatcher.java @@ -16,9 +16,12 @@ package android.os; +import android.annotation.IntDef; import android.util.Log; import android.util.proto.ProtoOutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** @@ -68,6 +71,17 @@ public class PatternMatcher implements Parcelable { */ public static final int PATTERN_SUFFIX = 4; + /** @hide */ + @IntDef(value = { + PATTERN_LITERAL, + PATTERN_PREFIX, + PATTERN_SIMPLE_GLOB, + PATTERN_ADVANCED_GLOB, + PATTERN_SUFFIX, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PatternType {} + // token types for advanced matching private static final int TOKEN_TYPE_LITERAL = 0; private static final int TOKEN_TYPE_ANY = 1; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 1f3a1620a9f2..3e7b6c9d361a 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -844,7 +844,7 @@ public class Process { return "amd64".equals(System.getProperty("os.arch")); } - private static ThreadLocal<SomeArgs> sIdentity$ravenwood; + private static volatile ThreadLocal<SomeArgs> sIdentity$ravenwood; /** @hide */ @android.ravenwood.annotation.RavenwoodKeep diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java index 5e7549fa67d8..4b16c1dce463 100644 --- a/core/java/android/os/TestLooperManager.java +++ b/core/java/android/os/TestLooperManager.java @@ -28,6 +28,7 @@ import java.util.concurrent.LinkedBlockingQueue; * The test code may use {@link #next()} to acquire messages that have been queued to this * {@link Looper}/{@link MessageQueue} and then {@link #execute} to run any that desires. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class TestLooperManager { private static final ArraySet<Looper> sHeldLoopers = new ArraySet<>(); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 9587db13ea87..3a57e84ee404 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1669,12 +1669,6 @@ public class StorageManager { } } - /** {@hide} */ - @TestApi - public static boolean isUserKeyUnlocked(int userId) { - return isCeStorageUnlocked(userId); - } - /** * Returns true if the user's credential-encrypted (CE) storage is unlocked. * diff --git a/core/java/android/provider/ContactKeysManager.java b/core/java/android/provider/ContactKeysManager.java new file mode 100644 index 000000000000..ecde69917820 --- /dev/null +++ b/core/java/android/provider/ContactKeysManager.java @@ -0,0 +1,1129 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.provider; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * ContactKeysManager provides the access to the E2EE contact keys provider. + * It manages two types of keys - {@link ContactKey} of other users' and the owner's keys - + * {@link SelfKey}. + * <ul> + * <li> + * For {@link ContactKey} this API allows the insert/update, removal, changing of the + * verification state, retrieving the keys (either created by or visible to the caller app) + * operations. + * </li> + * <li> + * For {@link SelfKey} this API allows the insert/update, removal, retrieving the self keys + * (either created by or visible to the caller app) operations. + * </li> + * </ul> + * Keys are uniquely identified by: + * <ul> + * <li> + * ownerPackageName - package name of an app that the key belongs to. + * </li> + * <li> + * deviceId - an app-specified identifier for the device, defined by the app. Within that app, + * the deviceID should be unique among devices belonging to that user. + * </li> + * <li> + * accountId - the app-specified identifier for the account for which the contact key can be used. + * Usually a phone number. + * </li> + * </ul> + * Contact keys also use lookupKey which is an opaque value used to identify a contact in + * ContactsProvider. + */ +@FlaggedApi(Flags.FLAG_USER_KEYS) +public class ContactKeysManager { + /** + * The authority for the contact keys provider. + * @hide + */ + public static final String AUTHORITY = "com.android.contactkeys.contactkeysprovider"; + + /** + * A content:// style uri to the authority for the contact keys provider. + * @hide + */ + @NonNull + public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); + + /** + * Maximum size of a contact key. + */ + private static final int MAX_KEY_SIZE_BYTES = 5000; + + /** + * Special value to distinguish a null array in a parcelable object. + */ + private static final int ARRAY_IS_NULL = -1; + + @NonNull + private final ContentResolver mContentResolver; + + /** @hide */ + public ContactKeysManager(@NonNull Context context) { + Objects.requireNonNull(context); + mContentResolver = context.getContentResolver(); + } + + /** + * Inserts a new entry into the contact keys table or updates one if it already exists. + * The inserted/updated contact key is owned by the caller app. + * + * @param lookupKey value that references the contact + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes) + */ + @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) + public void updateOrInsertContactKey(@NonNull String lookupKey, + @NonNull String deviceId, + @NonNull String accountId, + @NonNull byte[] keyValue) { + validateKeyLength(keyValue); + + Bundle extras = new Bundle(); + extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + extras.putByteArray(ContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue)); + + nullSafeCall(mContentResolver, + ContactKeys.UPDATE_OR_INSERT_CONTACT_KEY_METHOD, extras); + } + + /** + * Retrieves a contact key entry given the lookup key, device ID, accountId and inferred + * caller package name. + * + * @param lookupKey the value that references the contact + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * + * @return a {@link ContactKey} object containing the contact key information, + * or null if no contact key is found. + */ + @RequiresPermission(android.Manifest.permission.READ_CONTACTS) + @Nullable + public ContactKey getContactKey( + @NonNull String lookupKey, + @NonNull String deviceId, + @NonNull String accountId) { + Bundle extras = new Bundle(); + extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.GET_CONTACT_KEY_METHOD, extras); + + if (response == null) { + return null; + } + return response.getParcelable(ContactKeys.KEY_CONTACT_KEY, ContactKey.class); + } + + /** + * Retrieves all contact key entries that belong to apps visible to the caller. + * The keys will be stripped of deviceId, timeUpdated and keyValue data. + * + * @param lookupKey the value that references the contact + * + * @return a list of {@link ContactKey} objects containing the contact key + * information, or an empty list if no keys are found. + */ + @RequiresPermission(android.Manifest.permission.READ_CONTACTS) + @NonNull + public List<ContactKey> getAllContactKeys(@NonNull String lookupKey) { + Bundle extras = new Bundle(); + extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.GET_ALL_CONTACT_KEYS_METHOD, extras); + + if (response == null) { + return new ArrayList<>(); + } + List<ContactKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS, + ContactKey.class); + if (value == null) { + return new ArrayList<>(); + } + return value; + } + + /** + * Retrieves all contact key entries for a given lookupKey that belong to the caller app. + * + * @param lookupKey the value that references the contact + * + * @return a list of {@link ContactKey} objects containing the contact key + * information, or an empty list if no keys are found. + */ + @RequiresPermission(android.Manifest.permission.READ_CONTACTS) + @NonNull + public List<ContactKey> getOwnerContactKeys(@NonNull String lookupKey) { + Bundle extras = new Bundle(); + extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.GET_OWNER_CONTACT_KEYS_METHOD, extras); + + if (response == null) { + return new ArrayList<>(); + } + List<ContactKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS, + ContactKey.class); + if (value == null) { + return new ArrayList<>(); + } + return value; + } + + /** + * Updates a contact key entry's local verification state that belongs to the caller app. + * + * @param lookupKey the value that references the contact + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * @param localVerificationState the new local verification state + * + * @return true if the entry was updated, false otherwise. + */ + @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) + public boolean updateContactKeyLocalVerificationState(@NonNull String lookupKey, + @NonNull String deviceId, + @NonNull String accountId, + @VerificationState int localVerificationState) { + validateVerificationState(localVerificationState); + + Bundle extras = new Bundle(); + extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + extras.putInt(ContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras); + + return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS); + } + + /** + * Updates a contact key entry's remote verification state that belongs to the caller app. + * + * @param lookupKey the value that references the contact + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * @param remoteVerificationState the new remote verification state + * + * @return true if the entry was updated, false otherwise. + */ + @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) + public boolean updateContactKeyRemoteVerificationState(@NonNull String lookupKey, + @NonNull String deviceId, + @NonNull String accountId, + @VerificationState int remoteVerificationState) { + validateVerificationState(remoteVerificationState); + + Bundle extras = new Bundle(); + extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras); + + return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS); + } + + private static void validateVerificationState(int verificationState) { + if (verificationState != UNVERIFIED + && verificationState != VERIFICATION_FAILED + && verificationState != VERIFIED) { + throw new IllegalArgumentException("Verification state value " + + verificationState + " is not supported"); + } + } + + /** + * Removes a contact key entry that belongs to the caller app. + * + * @param lookupKey the value that references the contact + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * + * @return true if the entry was removed, false otherwise. + */ + @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) + public boolean removeContactKey(@NonNull String lookupKey, + @NonNull String deviceId, + @NonNull String accountId) { + Bundle extras = new Bundle(); + extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.REMOVE_CONTACT_KEY_METHOD, extras); + + return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS); + } + + /** + * Inserts a new entry into the self keys table or updates one if it already exists. + + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes) + * + * @return true if the entry was added or updated, false otherwise. + */ + @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) + public boolean updateOrInsertSelfKey(@NonNull String deviceId, + @NonNull String accountId, + @NonNull byte[] keyValue) { + validateKeyLength(keyValue); + + Bundle extras = new Bundle(); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + extras.putByteArray(ContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue)); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.UPDATE_OR_INSERT_SELF_KEY_METHOD, extras); + + return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS); + } + + private static void validateKeyLength(byte[] keyValue) { + Objects.requireNonNull(keyValue); + if (keyValue.length == 0 || keyValue.length > getMaxKeySizeBytes()) { + throw new IllegalArgumentException("Key value length is " + keyValue.length + "." + + " Should be more than 0 and less than " + getMaxKeySizeBytes()); + } + } + + /** + * Updates a self key entry's remote verification state. + * + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * @param remoteVerificationState the new remote verification state + * + * @return true if the entry was updated, false otherwise. + */ + @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) + public boolean updateSelfKeyRemoteVerificationState(@NonNull String deviceId, + @NonNull String accountId, + @VerificationState int remoteVerificationState) { + validateVerificationState(remoteVerificationState); + + Bundle extras = new Bundle(); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras); + + return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS); + } + + /** + * Maximum size of a contact key. + */ + public static int getMaxKeySizeBytes() { + return MAX_KEY_SIZE_BYTES; + } + + /** + * Returns a self key entry given the deviceId and the inferred package name of the caller. + * + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * + * @return a {@link SelfKey} object containing the self key information, or null if no self key + * is found. + */ + @RequiresPermission(android.Manifest.permission.READ_CONTACTS) + @Nullable + public SelfKey getSelfKey(@NonNull String deviceId, + @NonNull String accountId) { + Bundle extras = new Bundle(); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.GET_SELF_KEY_METHOD, extras); + + if (response == null) { + return null; + } + return response.getParcelable(ContactKeys.KEY_CONTACT_KEY, SelfKey.class); + } + + /** + * Returns all self key entries that belong to apps visible to the caller. + * The keys will be stripped of deviceId, timeUpdated and keyValue data. + * + * @return a list of {@link SelfKey} objects containing the self key information, or + * an empty list if no keys are found. + */ + @RequiresPermission(android.Manifest.permission.READ_CONTACTS) + @NonNull + public List<SelfKey> getAllSelfKeys() { + Bundle extras = new Bundle(); + + Bundle response = nullSafeCall(mContentResolver, ContactKeys.GET_ALL_SELF_KEYS_METHOD, + extras); + + if (response == null) { + return new ArrayList<>(); + } + List<SelfKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS, + SelfKey.class); + if (value == null) { + return new ArrayList<>(); + } + return value; + } + + /** + * Returns all self key entries that are owned by the caller app. + * + * @return a list of {@link SelfKey} objects containing the self key information, or + * an empty list if no keys are found. + */ + @RequiresPermission(android.Manifest.permission.READ_CONTACTS) + @NonNull + public List<SelfKey> getOwnerSelfKeys() { + Bundle extras = new Bundle(); + + Bundle response = nullSafeCall(mContentResolver, ContactKeys.GET_OWNER_SELF_KEYS_METHOD, + extras); + + if (response == null) { + return new ArrayList<>(); + } + List<SelfKey> value = response.getParcelableArrayList(ContactKeys.KEY_CONTACT_KEYS, + SelfKey.class); + if (value == null) { + return new ArrayList<>(); + } + return value; + } + + /** + * Removes a self key entry given the deviceId and the inferred package name of the caller. + + * @param deviceId an app-specified identifier for the device + * @param accountId an app-specified identifier for the account + * + * @return true if the entry was removed, false otherwise. + */ + @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) + public boolean removeSelfKey(@NonNull String deviceId, + @NonNull String accountId) { + Bundle extras = new Bundle(); + extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); + extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); + + Bundle response = nullSafeCall(mContentResolver, + ContactKeys.REMOVE_SELF_KEY_METHOD, extras); + + return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS); + } + + private Bundle nullSafeCall(@NonNull ContentResolver resolver, @NonNull String method, + @Nullable Bundle extras) { + try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY_URI)) { + return client.call(method, null, extras); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Possible values of verification state. + * @hide + */ + @IntDef(prefix = {"VERIFICATION_STATE_"}, value = { + UNVERIFIED, + VERIFICATION_FAILED, + VERIFIED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface VerificationState {} + + /** + * Unverified state of a contact E2EE key. + */ + public static final int UNVERIFIED = 0; + /** + * Failed verification state of a contact E2EE key. + */ + public static final int VERIFICATION_FAILED = 1; + /** + * Verified state of a contact E2EE key. + */ + public static final int VERIFIED = 2; + + /** @hide */ + public static final class ContactKeys { + + private ContactKeys() {} + + /** + * <p> + * An opaque value that contains hints on how to find the contact if + * its row id changed as a result of a sync or aggregation. + * </p> + */ + public static final String LOOKUP_KEY = "lookup"; + + /** + * <p> + * An app-specified identifier for the device for which the contact key can be used. + * </p> + */ + public static final String DEVICE_ID = "device_id"; + + /** + * <p> + * An app-specified identifier for the account for which the contact key can be used. + * Usually a phone number. + * </p> + */ + public static final String ACCOUNT_ID = "account_id"; + + /** + * <p> + * The display name for the contact. + * </p> + */ + public static final String DISPLAY_NAME = "display_name"; + + /** + * <p> + * The phone number as the user entered it. + * </p> + */ + public static final String PHONE_NUMBER = "number"; + + /** + * <p> + * The email address. + * </p> + */ + public static final String EMAIL_ADDRESS = "address"; + + /** + * <p> + * Timestamp at which the key was updated. + * </p> + */ + public static final String TIME_UPDATED = "time_updated"; + + /** + * <p> + * The raw bytes for the key. + * </p> + */ + public static final String KEY_VALUE = "key_value"; + + /** + * <p> + * The package name of the package that created the key. + * </p> + */ + public static final String OWNER_PACKAGE_NAME = "owner_package_name"; + + /** + * <p> + * Describes the local verification state for the key, for instance QR-code based + * verification. + * </p> + */ + public static final String LOCAL_VERIFICATION_STATE = "local_verification_state"; + + /** + * <p> + * Describes the remote verification state for the key, for instance through a key + * transparency server. + * </p> + */ + public static final String REMOTE_VERIFICATION_STATE = "remote_verification_state"; + + /** + * The method to invoke in order to add a new key for a contact. + */ + public static final String UPDATE_OR_INSERT_CONTACT_KEY_METHOD = "updateOrInsertContactKey"; + + /** + * The method to invoke in order to retrieve key for a single contact. + */ + public static final String GET_CONTACT_KEY_METHOD = "getContactKey"; + + /** + * The method to invoke in order to retrieve all contact keys. + */ + public static final String GET_ALL_CONTACT_KEYS_METHOD = "getAllContactKeys"; + + /** + * The method to invoke in order to retrieve contact keys that belong to the caller. + */ + public static final String GET_OWNER_CONTACT_KEYS_METHOD = "getOwnerContactKeys"; + + /** + * The method to invoke in order to update a contact key local verification state. + */ + public static final String UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD = + "updateContactKeyLocalVerificationState"; + + /** + * The method to invoke in order to update a contact key remote verification state. + */ + public static final String UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD = + "updateContactKeyRemoteVerificationState"; + + /** + * The method to invoke in order to remove a contact key. + */ + public static final String REMOVE_CONTACT_KEY_METHOD = "removeContactKey"; + + /** + * The method to invoke in order to add a new self key. + */ + public static final String UPDATE_OR_INSERT_SELF_KEY_METHOD = "updateOrInsertSelfKey"; + + /** + * The method to invoke in order to update a self key remote verification state. + */ + public static final String UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD = + "updateSelfKeyRemoteVerificationState"; + + /** + * The method to invoke in order to retrieve a self key. + */ + public static final String GET_SELF_KEY_METHOD = "getSelfKey"; + + /** + * The method to invoke in order to retrieve all self keys. + */ + public static final String GET_ALL_SELF_KEYS_METHOD = "getAllSelfKeys"; + + /** + * The method to invoke in order to retrieve self keys that belong to the caller. + */ + public static final String GET_OWNER_SELF_KEYS_METHOD = "getOwnerSelfKeys"; + + /** + * The method to invoke in order to remove a new self key. + */ + public static final String REMOVE_SELF_KEY_METHOD = "removeSelfKey"; + + /** + * Key in the incoming Bundle for all the contact keys. + */ + public static final String KEY_CONTACT_KEYS = "key_contact_keys"; + + /** + * Key in the incoming Bundle for a single contact key. + */ + public static final String KEY_CONTACT_KEY = "key_contact_key"; + + /** + * Key in the incoming Bundle for a number of modified rows. + */ + public static final String KEY_UPDATED_ROWS = "key_updated_rows"; + } + + /** + * A parcelable class encapsulating other users' E2EE contact key. + */ + public static final class ContactKey implements Parcelable { + /** + * An app-specified identifier for the device for which the contact key can be used. + */ + private final String mDeviceId; + + /** + * An app-specified identifier for the account for which the contact key can be used. + * Usually a phone number. + */ + private final String mAccountId; + + /** + * Owner application package name. + */ + private final String mOwnerPackageName; + + /** + * Timestamp at which the key was updated. + */ + private final long mTimeUpdated; + + /** + * The raw bytes for the key. + */ + private final byte[] mKeyValue; + + /** + * Describes the local verification state for the key, for instance QR-code based + * verification. + */ + private final int mLocalVerificationState; + + /** + * Describes the remote verification state for the key, for instance through a key + * transparency server. + */ + private final int mRemoteVerificationState; + + /** + * The display name for the contact. + */ + private final String mDisplayName; + + /** + * The phone number as the user entered it. + */ + private final String mPhoneNumber; + + /** + * The email address. + */ + private final String mEmailAddress; + + /** + * @hide + */ + public ContactKey(@Nullable String deviceId, @NonNull String accountId, + @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, + @VerificationState int localVerificationState, + @VerificationState int remoteVerificationState, + @Nullable String displayName, + @Nullable String phoneNumber, @Nullable String emailAddress) { + this.mDeviceId = deviceId; + this.mAccountId = accountId; + this.mOwnerPackageName = ownerPackageName; + this.mTimeUpdated = timeUpdated; + this.mKeyValue = keyValue == null ? null : Arrays.copyOf(keyValue, keyValue.length); + this.mLocalVerificationState = localVerificationState; + this.mRemoteVerificationState = remoteVerificationState; + this.mDisplayName = displayName; + this.mPhoneNumber = phoneNumber; + this.mEmailAddress = emailAddress; + } + + /** + * Gets the app-specified identifier for the device for which the contact key can be used. + * Returns null if the app doesn't have the required visibility into the contact key. + * + * @return An app-specified identifier for the device. + */ + @Nullable + public String getDeviceId() { + return mDeviceId; + } + + /** + * Gets the app-specified identifier for the account for which the contact key can be used. + * Usually a phone number. + * + * @return An app-specified identifier for the account. + */ + @NonNull + public String getAccountId() { + return mAccountId; + } + + /** + * Gets the owner application package name. + * + * @return The owner application package name. + */ + @NonNull + public String getOwnerPackageName() { + return mOwnerPackageName; + } + + /** + * Gets the timestamp at which the key was updated. Returns -1 if the app doesn't have the + * required visibility into the contact key. + * + * @return The timestamp at which the key was updated in the System.currentTimeMillis() + * base. + */ + public long getTimeUpdated() { + return mTimeUpdated; + } + + /** + * Gets the raw bytes for the key. + * Returns null if the app doesn't have the required visibility into the contact key. + * + * @return A copy of the raw bytes for the key. + */ + @Nullable + public byte[] getKeyValue() { + return mKeyValue == null ? null : Arrays.copyOf(mKeyValue, mKeyValue.length); + } + + /** + * Gets the local verification state for the key, for instance QR-code based verification. + * + * @return The local verification state for the key. + */ + public @VerificationState int getLocalVerificationState() { + return mLocalVerificationState; + } + + /** + * Gets the remote verification state for the key, for instance through a key transparency + * server. + * + * @return The remote verification state for the key. + */ + public @VerificationState int getRemoteVerificationState() { + return mRemoteVerificationState; + } + + /** + * Gets the display name for the contact. + * + * @return The display name for the contact. + */ + @Nullable + public String getDisplayName() { + return mDisplayName; + } + + /** + * Gets the phone number as the user entered it. + * + * @return The phone number as the user entered it. + */ + @Nullable + public String getPhoneNumber() { + return mPhoneNumber; + } + + /** + * Gets the email address. + * + * @return The email address. + */ + @Nullable + public String getEmailAddress() { + return mEmailAddress; + } + + @Override + public int hashCode() { + return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated, + Arrays.hashCode(mKeyValue), mLocalVerificationState, mRemoteVerificationState, + mDisplayName, mPhoneNumber, mEmailAddress); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj == this) return true; + + if (!(obj instanceof ContactKey toCompare)) { + return false; + } + + return Objects.equals(mDeviceId, toCompare.mDeviceId) + && Objects.equals(mAccountId, toCompare.mAccountId) + && Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName) + && mTimeUpdated == toCompare.mTimeUpdated + && Arrays.equals(mKeyValue, toCompare.mKeyValue) + && mLocalVerificationState == toCompare.mLocalVerificationState + && mRemoteVerificationState == toCompare.mRemoteVerificationState + && Objects.equals(mDisplayName, toCompare.mDisplayName) + && Objects.equals(mPhoneNumber, toCompare.mPhoneNumber) + && Objects.equals(mEmailAddress, toCompare.mEmailAddress); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mDeviceId); + dest.writeString8(mAccountId); + dest.writeString8(mOwnerPackageName); + dest.writeLong(mTimeUpdated); + dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL); + if (mKeyValue != null) { + dest.writeByteArray(mKeyValue); + } + dest.writeInt(mLocalVerificationState); + dest.writeInt(mRemoteVerificationState); + dest.writeString8(mDisplayName); + dest.writeString8(mPhoneNumber); + dest.writeString8(mEmailAddress); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<ContactKey> CREATOR = + new Creator<>() { + @Override + public ContactKey createFromParcel(Parcel source) { + String deviceId = source.readString8(); + String accountId = source.readString8(); + String ownerPackageName = source.readString8(); + long timeUpdated = source.readLong(); + int keyValueLength = source.readInt(); + byte[] keyValue; + if (keyValueLength > 0) { + keyValue = new byte[keyValueLength]; + source.readByteArray(keyValue); + } else { + keyValue = null; + } + int localVerificationState = source.readInt(); + int remoteVerificationState = source.readInt(); + String displayName = source.readString8(); + String number = source.readString8(); + String address = source.readString8(); + return new ContactKey(deviceId, accountId, ownerPackageName, + timeUpdated, keyValue, localVerificationState, + remoteVerificationState, displayName, number, address); + } + + @Override + public ContactKey[] newArray(int size) { + return new ContactKey[size]; + } + }; + } + + /** + * A parcelable class encapsulating self E2EE contact key. + */ + public static final class SelfKey implements Parcelable { + /** + * An app-specified identifier for the device for which the contact key can be used. + */ + private final String mDeviceId; + + /** + * An app-specified identifier for the account for which the contact key can be used. + * Usually a phone number. + */ + private final String mAccountId; + + /** + * Owner application package name. + */ + private final String mOwnerPackageName; + + /** + * Timestamp at which the key was updated. + */ + private final long mTimeUpdated; + + /** + * The raw bytes for the key. + */ + private final byte[] mKeyValue; + + + /** + * Describes the remote verification state for the key, for instance through a key + * transparency server. + */ + private final int mRemoteVerificationState; + + /** + * @hide + */ + public SelfKey(@Nullable String deviceId, @NonNull String accountId, + @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, + @VerificationState int remoteVerificationState) { + this.mDeviceId = deviceId; + this.mAccountId = accountId; + this.mOwnerPackageName = ownerPackageName; + this.mTimeUpdated = timeUpdated; + this.mKeyValue = keyValue == null ? null : Arrays.copyOf(keyValue, keyValue.length); + this.mRemoteVerificationState = remoteVerificationState; + } + + /** + * Gets the app-specified identifier for the device for which the contact key can be used. + * Returns null if the app doesn't have the required visibility into the contact key. + * + * @return An app-specified identifier for the device. + */ + @Nullable + public String getDeviceId() { + return mDeviceId; + } + + /** + * Gets the app-specified identifier for the account for which the contact key can be used. + * Usually a phone number. + * + * @return An app-specified identifier for the device. + */ + @NonNull + public String getAccountId() { + return mAccountId; + } + + /** + * Gets the owner application package name. + * + * @return The owner application package name. + */ + @NonNull + public String getOwnerPackageName() { + return mOwnerPackageName; + } + + /** + * Gets the timestamp at which the key was updated. Returns -1 if the app doesn't have the + * required visibility into the contact key. + * + * @return The timestamp at which the key was updated in the System.currentTimeMillis() + * base. + */ + public long getTimeUpdated() { + return mTimeUpdated; + } + + /** + * Gets the raw bytes for the key. + * Returns null if the app doesn't have the required visibility into the contact key. + * + * @return A copy of the raw bytes for the key. + */ + @Nullable + public byte[] getKeyValue() { + return mKeyValue == null ? null : Arrays.copyOf(mKeyValue, mKeyValue.length); + } + + /** + * Gets the remote verification state for the key, for instance through a key transparency + * server. + * + * @return The remote verification state for the key. + */ + public @VerificationState int getRemoteVerificationState() { + return mRemoteVerificationState; + } + + @Override + public int hashCode() { + return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated, + Arrays.hashCode(mKeyValue), mRemoteVerificationState); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (obj == this) return true; + + if (!(obj instanceof SelfKey toCompare)) { + return false; + } + + return Objects.equals(mDeviceId, toCompare.mDeviceId) + && Objects.equals(mAccountId, toCompare.mAccountId) + && Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName) + && mTimeUpdated == toCompare.mTimeUpdated + && Arrays.equals(mKeyValue, toCompare.mKeyValue) + && mRemoteVerificationState == toCompare.mRemoteVerificationState; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mDeviceId); + dest.writeString8(mAccountId); + dest.writeString8(mOwnerPackageName); + dest.writeLong(mTimeUpdated); + dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL); + if (mKeyValue != null) { + dest.writeByteArray(mKeyValue); + } + dest.writeInt(mRemoteVerificationState); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<SelfKey> CREATOR = + new Creator<>() { + @Override + public SelfKey createFromParcel(Parcel source) { + String deviceId = source.readString8(); + String accountId = source.readString8(); + String ownerPackageName = source.readString8(); + long timeUpdated = source.readLong(); + int keyValueLength = source.readInt(); + byte[] keyValue; + if (keyValueLength > 0) { + keyValue = new byte[keyValueLength]; + source.readByteArray(keyValue); + } else { + keyValue = null; + } + int remoteVerificationState = source.readInt(); + return new SelfKey(deviceId, accountId, ownerPackageName, + timeUpdated, keyValue, remoteVerificationState); + } + + @Override + public SelfKey[] newArray(int size) { + return new SelfKey[size]; + } + }; + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c7692d58ca54..524b7336718f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4932,6 +4932,7 @@ public final class Settings { * * @hide */ + @Readable public static final String DEFAULT_DEVICE_FONT_SCALE = "device_font_scale"; /** diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index d47ff2e2cd99..af688e92fb36 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4927,7 +4927,7 @@ public final class Telephony { /** * TelephonyProvider column name for satellite attach enabled for carrier. The value of this * column is set based on user settings. - * By default, it's disabled. + * By default, it's enabled. * * @hide */ diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig index 3dd7692cd70d..0f12b1397c5b 100644 --- a/core/java/android/provider/flags.aconfig +++ b/core/java/android/provider/flags.aconfig @@ -6,3 +6,10 @@ flag { description: "Enable Settings.System.resetToDefault APIs." bug: "279083734" } + +flag { + name: "user_keys" + namespace: "privacy_infra_policy" + description: "This flag controls new E2EE contact keys API" + bug: "290696572" +} diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig index fe6c4a4321e9..0bae459fefc3 100644 --- a/core/java/android/security/responsible_apis_flags.aconfig +++ b/core/java/android/security/responsible_apis_flags.aconfig @@ -27,3 +27,17 @@ flag { description: "Enables the content URI permission APIs" bug: "293467489" } + +flag { + name: "enforce_intent_filter_match" + namespace: "responsible_apis" + description: "Make delivered intents match components' intent filters" + bug: "293560872" +} + +flag { + name: "block_null_action_intents" + namespace: "responsible_apis" + description: "Do not allow intents without an action to match any intent filters" + bug: "293560872" +} diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 9d19ef6bdc64..d4a5356d99c6 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -2297,6 +2297,18 @@ public class ZenModeConfig implements Parcelable { component, configurationActivity, pkg, id, enabler, zenPolicy, modified); } + /** Returns a deep copy of the {@link ZenRule}. */ + public ZenRule copy() { + final Parcel parcel = Parcel.obtain(); + try { + writeToParcel(parcel, 0); + parcel.setDataPosition(0); + return new ZenRule(parcel); + } finally { + parcel.recycle(); + } + } + public boolean isAutomaticActive() { return enabled && !snoozing && getPkg() != null && isTrueOrUnknown(); } diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig index 3008b8d45252..446fe3de6482 100644 --- a/core/java/android/service/notification/flags.aconfig +++ b/core/java/android/service/notification/flags.aconfig @@ -8,14 +8,6 @@ flag { } flag { - name: "notification_lifetime_extension_refactor" - namespace: "systemui" - description: "Enables moving notification lifetime extension management from SystemUI to " - "Notification Manager Service" - bug: "299448097" -} - -flag { name: "redact_sensitive_notifications_from_untrusted_listeners" namespace: "systemui" description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices" diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 2105420b84cd..99bd2ff7134c 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -30,6 +30,7 @@ import android.graphics.RectF; import android.graphics.text.LineBreakConfig; import android.graphics.text.LineBreaker; import android.os.Build; +import android.os.Trace; import android.text.style.LeadingMarginSpan; import android.text.style.LeadingMarginSpan.LeadingMarginSpan2; import android.text.style.LineHeightSpan; @@ -534,7 +535,12 @@ public class StaticLayout extends Layout { if (recycle == null) { recycle = new StaticLayout(); } - recycle.generate(this, mIncludePad, trackpadding); + Trace.beginSection("Generating StaticLayout For DynamicLayout"); + try { + recycle.generate(this, mIncludePad, trackpadding); + } finally { + Trace.endSection(); + } return recycle; } @@ -689,7 +695,12 @@ public class StaticLayout extends Layout { mLeftIndents = b.mLeftIndents; mRightIndents = b.mRightIndents; - generate(b, b.mIncludePad, trackPadding); + Trace.beginSection("Constructing StaticLayout"); + try { + generate(b, b.mIncludePad, trackPadding); + } finally { + Trace.endSection(); + } } private static int getBaseHyphenationFrequency(int frequency) { diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index f28574ecb3b2..27c509a8605f 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -197,9 +197,28 @@ public interface AttachedSurfaceControl { * Transfer the currently in progress touch gesture from the host to the requested * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the * SurfaceControlViewHost was created with the current host's inputToken. + * <p> + * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL + * and does not receive any further input events for this gesture. + * <p> + * The transferred-to window receives an ACTION_DOWN event and then the remainder of the + * input events for this gesture. It does not receive any of the previous events of this gesture + * that the originating window received. + * <p> + * The "transferTouch" API only works for the current gesture. When a new gesture arrives, + * input dispatcher will do a new round of hit testing. So, if the "host" window is still the + * first thing that's being touched, then it will receive the new gesture again. It will + * again be up to the host to transfer this new gesture to the embedded. + * <p> + * Once the transferred-to window receives the gesture, it can choose to give up this gesture + * and send it to another window that it's linked to (it can't be an arbitrary window for + * security reasons) using the same transferTouch API. Only the window currently receiving + * touch is allowed to transfer the gesture. * * @param surfacePackage The SurfacePackage to transfer the gesture to. * @return Whether the touch stream was transferred. + * @see SurfaceControlViewHost#transferTouchGestureToHost() for the reverse to transfer touch + * gesture from the embedded to the host. */ @FlaggedApi(Flags.FLAG_TRANSFER_GESTURE_TO_EMBEDDED) default boolean transferHostTouchGestureToEmbedded( diff --git a/core/java/android/view/MotionPredictor.java b/core/java/android/view/MotionPredictor.java index 27af3000fc8b..db2efaa81ef3 100644 --- a/core/java/android/view/MotionPredictor.java +++ b/core/java/android/view/MotionPredictor.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import com.android.internal.annotations.VisibleForTesting; + import libcore.util.NativeAllocationRegistry; /** @@ -57,11 +59,21 @@ public final class MotionPredictor { * @param context The context for the predictions */ public MotionPredictor(@NonNull Context context) { - mIsPredictionEnabled = context.getResources().getBoolean( - com.android.internal.R.bool.config_enableMotionPrediction); - final int offsetNanos = context.getResources().getInteger( - com.android.internal.R.integer.config_motionPredictionOffsetNanos); - mPtr = nativeInitialize(offsetNanos); + this( + context.getResources().getBoolean( + com.android.internal.R.bool.config_enableMotionPrediction), + context.getResources().getInteger( + com.android.internal.R.integer.config_motionPredictionOffsetNanos)); + } + + /** + * Internal constructor for testing. + * @hide + */ + @VisibleForTesting + public MotionPredictor(boolean isPredictionEnabled, int motionPredictionOffsetNanos) { + mIsPredictionEnabled = isPredictionEnabled; + mPtr = nativeInitialize(motionPredictionOffsetNanos); RegistryHolder.REGISTRY.registerNativeAllocation(this, mPtr); } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index a1f44e4c9622..6c6e8b247886 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -1073,8 +1073,7 @@ public class Surface implements Parcelable { if (error == -EINVAL) { throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()"); } else if (error != 0) { - throw new RuntimeException("Failed to set frame rate on Surface. Native error: " - + error); + Log.e(TAG, "Failed to set frame rate on Surface. Native error: " + error); } } } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 4840f003da3e..5249fd5253f2 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -538,8 +538,8 @@ public class SurfaceControlViewHost { } private void addWindowToken(WindowManager.LayoutParams attrs) { - final WindowManagerImpl wm = - (WindowManagerImpl) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE); + final WindowManager wm = + (WindowManager) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE); attrs.token = wm.getDefaultToken(); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 42355bb17c2d..c78826116426 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -6022,8 +6022,8 @@ public interface WindowManager extends ViewManager { * This is different from * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, * SurfaceControlInputReceiver)} in that the input events are received batched. The caller must - * invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources when - * no longer needing to use the {@link SurfaceControlInputReceiver} + * invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the + * resources when no longer needing to use the {@link SurfaceControlInputReceiver} * * @param displayId The display that the SurfaceControl will be placed on. Input will * only work @@ -6035,14 +6035,9 @@ public interface WindowManager extends ViewManager { * @param choreographer The Choreographer used for batching. This should match the rendering * Choreographer. * @param receiver The SurfaceControlInputReceiver that will receive the input events - * @return an {@link IBinder} token that is used to unregister the input receiver via - * {@link #unregisterSurfaceControlInputReceiver(IBinder)}. - * @see #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, - * SurfaceControlInputReceiver) */ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) - @NonNull - default IBinder registerBatchedSurfaceControlInputReceiver(int displayId, + default void registerBatchedSurfaceControlInputReceiver(int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { throw new UnsupportedOperationException( @@ -6054,8 +6049,8 @@ public interface WindowManager extends ViewManager { * receive every input event. This is different than calling @link * #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer, * SurfaceControlInputReceiver)} in that the input events are received unbatched. The caller - * must invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources - * when no longer needing to use the {@link SurfaceControlInputReceiver} + * must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the + * resources when no longer needing to use the {@link SurfaceControlInputReceiver} * * @param displayId The display that the SurfaceControl will be placed on. Input will only * work if SurfaceControl is on that display and that display was @@ -6066,14 +6061,9 @@ public interface WindowManager extends ViewManager { * @param surfaceControl The SurfaceControl to register the InputChannel for * @param looper The looper to use when invoking callbacks. * @param receiver The SurfaceControlInputReceiver that will receive the input events - * @return an {@link IBinder} token that is used to unregister the input receiver via - * {@link #unregisterSurfaceControlInputReceiver(IBinder)}. - * @see #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer, - * SurfaceControlInputReceiver) **/ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) - @NonNull - default IBinder registerUnbatchedSurfaceControlInputReceiver(int displayId, + default void registerUnbatchedSurfaceControlInputReceiver(int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { throw new UnsupportedOperationException( @@ -6091,17 +6081,40 @@ public interface WindowManager extends ViewManager { * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, * SurfaceControlInputReceiver)} * - * @param token The token that was returned via - * {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder, - * SurfaceControl, - * Choreographer, SurfaceControlInputReceiver)} or - * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, - * SurfaceControl, - * Looper, SurfaceControlInputReceiver)} + * @param surfaceControl The SurfaceControl to remove and unregister the input channel for. */ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) - default void unregisterSurfaceControlInputReceiver(@NonNull IBinder token) { + default void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) { throw new UnsupportedOperationException( "unregisterSurfaceControlInputReceiver is not implemented"); } + + /** + * Returns the input client token for the {@link SurfaceControl}. This will only return non null + * if the SurfaceControl was registered for input via + * { #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer, + * SurfaceControlInputReceiver)} or + * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper, + * SurfaceControlInputReceiver)}. + * <p> + * This is helpful for testing to ensure the test waits for the layer to be registered with + * SurfaceFlinger and Input before proceeding with the test. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) + @TestApi + @Nullable + default IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) { + throw new UnsupportedOperationException( + "getSurfaceControlInputClientToken is not implemented"); + } + + /** + * @hide + */ + default @NonNull IBinder getDefaultToken() { + throw new UnsupportedOperationException( + "getDefaultToken is not implemented"); + } } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 8d40f9a4f7b1..c49fce5b558f 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -38,10 +38,12 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Pair; +import android.util.SparseArray; import android.view.inputmethod.InputMethodManager; import android.window.ITrustedPresentationListener; import android.window.TrustedPresentationThresholds; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FastPrintWriter; import java.io.FileDescriptor; @@ -50,7 +52,6 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.IntConsumer; @@ -156,8 +157,9 @@ public final class WindowManagerGlobal { private final TrustedPresentationListener mTrustedPresentationListener = new TrustedPresentationListener(); - private final ConcurrentHashMap<IBinder, InputEventReceiver> mSurfaceControlInputReceivers = - new ConcurrentHashMap<>(); + @GuardedBy("mSurfaceControlInputReceivers") + private final SparseArray<SurfaceControlInputReceiverInfo> + mSurfaceControlInputReceivers = new SparseArray<>(); private WindowManagerGlobal() { } @@ -816,7 +818,7 @@ public final class WindowManagerGlobal { mTrustedPresentationListener.removeListener(listener); } - IBinder registerBatchedSurfaceControlInputReceiver(int displayId, + void registerBatchedSurfaceControlInputReceiver(int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { IBinder clientToken = new Binder(); @@ -830,19 +832,21 @@ public final class WindowManagerGlobal { e.rethrowAsRuntimeException(); } - mSurfaceControlInputReceivers.put(clientToken, - new BatchedInputEventReceiver(inputChannel, choreographer.getLooper(), - choreographer) { - @Override - public void onInputEvent(InputEvent event) { - boolean handled = receiver.onInputEvent(event); - finishInputEvent(event, handled); - } - }); - return clientToken; + synchronized (mSurfaceControlInputReceivers) { + mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(), + new SurfaceControlInputReceiverInfo(clientToken, + new BatchedInputEventReceiver(inputChannel, choreographer.getLooper(), + choreographer) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = receiver.onInputEvent(event); + finishInputEvent(event, handled); + } + })); + } } - IBinder registerUnbatchedSurfaceControlInputReceiver( + void registerUnbatchedSurfaceControlInputReceiver( int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { IBinder clientToken = new Binder(); @@ -856,32 +860,53 @@ public final class WindowManagerGlobal { e.rethrowAsRuntimeException(); } - mSurfaceControlInputReceivers.put(clientToken, - new InputEventReceiver(inputChannel, looper) { - @Override - public void onInputEvent(InputEvent event) { - boolean handled = receiver.onInputEvent(event); - finishInputEvent(event, handled); - } - }); - - return clientToken; + synchronized (mSurfaceControlInputReceivers) { + mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(), + new SurfaceControlInputReceiverInfo(clientToken, + new InputEventReceiver(inputChannel, looper) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = receiver.onInputEvent(event); + finishInputEvent(event, handled); + } + })); + } } - void unregisterSurfaceControlInputReceiver(IBinder token) { - InputEventReceiver inputEventReceiver = mSurfaceControlInputReceivers.get(token); - if (inputEventReceiver == null) { - Log.w(TAG, "No registered input event receiver with token: " + token); + void unregisterSurfaceControlInputReceiver(SurfaceControl surfaceControl) { + SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo; + synchronized (mSurfaceControlInputReceivers) { + surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.removeReturnOld( + surfaceControl.getLayerId()); + } + + if (surfaceControlInputReceiverInfo == null) { + Log.w(TAG, "No registered input event receiver with sc: " + surfaceControl); return; } try { - WindowManagerGlobal.getWindowSession().remove(token); + WindowManagerGlobal.getWindowSession().remove( + surfaceControlInputReceiverInfo.mClientToken); } catch (RemoteException e) { Log.e(TAG, "Failed to remove input channel", e); e.rethrowAsRuntimeException(); } - inputEventReceiver.dispose(); + surfaceControlInputReceiverInfo.mInputEventReceiver.dispose(); + } + + IBinder getSurfaceControlInputClientToken(SurfaceControl surfaceControl) { + SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo; + synchronized (mSurfaceControlInputReceivers) { + surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.get( + surfaceControl.getLayerId()); + } + + if (surfaceControlInputReceiverInfo == null) { + Log.w(TAG, "No registered input event receiver with sc: " + surfaceControl); + return null; + } + return surfaceControlInputReceiverInfo.mClientToken; } private final class TrustedPresentationListener extends @@ -976,6 +1001,17 @@ public final class WindowManagerGlobal { throw e.rethrowFromSystemServer(); } } + + private static class SurfaceControlInputReceiverInfo { + final IBinder mClientToken; + final InputEventReceiver mInputEventReceiver; + + private SurfaceControlInputReceiverInfo(IBinder clientToken, + InputEventReceiver inputEventReceiver) { + mClientToken = clientToken; + mInputEventReceiver = inputEventReceiver; + } + } } final class WindowLeaked extends AndroidRuntimeException { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index aaf5fcc6f095..5072ad755cba 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -458,7 +458,9 @@ public final class WindowManagerImpl implements WindowManager { return null; } - IBinder getDefaultToken() { + @Override + @NonNull + public IBinder getDefaultToken() { return mDefaultToken; } @@ -523,26 +525,30 @@ public final class WindowManagerImpl implements WindowManager { mGlobal.unregisterTrustedPresentationListener(listener); } - @NonNull @Override - public IBinder registerBatchedSurfaceControlInputReceiver(int displayId, + public void registerBatchedSurfaceControlInputReceiver(int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) { - return mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostToken, + mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostToken, surfaceControl, choreographer, receiver); } - @NonNull @Override - public IBinder registerUnbatchedSurfaceControlInputReceiver( + public void registerUnbatchedSurfaceControlInputReceiver( int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl, @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) { - return mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, hostToken, + mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, hostToken, surfaceControl, looper, receiver); } @Override - public void unregisterSurfaceControlInputReceiver(@NonNull IBinder token) { - mGlobal.unregisterSurfaceControlInputReceiver(token); + public void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) { + mGlobal.unregisterSurfaceControlInputReceiver(surfaceControl); + } + + @Override + @Nullable + public IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) { + return mGlobal.getSurfaceControlInputClientToken(surfaceControl); } } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 49d2ceb8fecf..ef1bf5a5c548 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -62,10 +62,12 @@ import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; import android.view.IWindow; +import android.view.SurfaceControl; import android.view.View; import android.view.accessibility.AccessibilityEvent.EventType; import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IntPair; @@ -155,22 +157,6 @@ public final class AccessibilityManager { public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON = "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON"; - /** - * Used as an int value for accessibility chooser activity to represent the accessibility button - * shortcut type. - * - * @hide - */ - public static final int ACCESSIBILITY_BUTTON = 0; - - /** - * Used as an int value for accessibility chooser activity to represent hardware key shortcut, - * such as volume key button. - * - * @hide - */ - public static final int ACCESSIBILITY_SHORTCUT_KEY = 1; - /** @hide */ public static final int FLASH_REASON_CALL = 1; @@ -184,32 +170,6 @@ public final class AccessibilityManager { public static final int FLASH_REASON_PREVIEW = 4; /** - * Annotations for the shortcut type. - * <p>Note: Keep in sync with {@link #SHORTCUT_TYPES}.</p> - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - // LINT.IfChange(shortcut_type_intdef) - ACCESSIBILITY_BUTTON, - ACCESSIBILITY_SHORTCUT_KEY - // LINT.ThenChange(:shortcut_type_array) - }) - public @interface ShortcutType {} - - /** - * Used for iterating through {@link ShortcutType}. - * <p>Note: Keep in sync with {@link ShortcutType}.</p> - * @hide - */ - public static final int[] SHORTCUT_TYPES = { - // LINT.IfChange(shortcut_type_array) - ACCESSIBILITY_BUTTON, - ACCESSIBILITY_SHORTCUT_KEY, - // LINT.ThenChange(:shortcut_type_intdef) - }; - - /** * Annotations for content flag of UI. * @hide */ @@ -1785,7 +1745,8 @@ public final class AccessibilityManager { @TestApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) @NonNull - public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) { + public List<String> getAccessibilityShortcutTargets( + @ShortcutConstants.UserShortcutType int shortcutType) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); @@ -2444,4 +2405,29 @@ public final class AccessibilityManager { throw re.rethrowFromSystemServer(); } } + + + /** + * Attaches a {@link android.view.SurfaceControl} containing an accessibility overlay to the + * specified display. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) + public void attachAccessibilityOverlayToDisplay( + int displayId, @NonNull SurfaceControl surfaceControl) { + final IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.attachAccessibilityOverlayToDisplay_enforcePermission( + displayId, surfaceControl); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 9c04c27d189a..1c5d29e0ff1e 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -31,6 +31,7 @@ import android.view.accessibility.IMagnificationConnection; import android.view.InputEvent; import android.view.IWindow; import android.view.MagnificationSpec; +import android.view.SurfaceControl; /** * Interface implemented by the AccessibilityManagerService called by @@ -136,4 +137,7 @@ interface IAccessibilityManager { MagnificationSpec magnificationSpec; } WindowTransformationSpec getWindowTransformationSpec(int windowId); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW)") + void attachAccessibilityOverlayToDisplay_enforcePermission(int displayId, in SurfaceControl surfaceControl); } diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig index 2a3008a53635..5d3153c00e8a 100644 --- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig +++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig @@ -34,3 +34,10 @@ flag { description: "If true, an appop is logged when a notification is rapidly cleared by a notification listener." bug: "289080543" } + +flag { + name: "manage_device_policy_enabled" + namespace: "content_protection" + description: "If true, the APIs to manage content protection device policy will be enabled." + bug: "319477846" +} diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java index befb0023ebe6..544642811a39 100644 --- a/core/java/android/window/ScreenCapture.java +++ b/core/java/android/window/ScreenCapture.java @@ -30,6 +30,8 @@ import android.os.Parcelable; import android.util.Log; import android.view.SurfaceControl; +import com.android.window.flags.Flags; + import libcore.util.NativeAllocationRegistry; import java.util.concurrent.CountDownLatch; @@ -48,7 +50,7 @@ public class ScreenCapture { private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs, long captureListener); private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs, - long captureListener); + long captureListener, boolean sync); private static native long nativeCreateScreenCaptureListener( ObjIntConsumer<ScreenshotHardwareBuffer> consumer); private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out); @@ -134,7 +136,8 @@ public class ScreenCapture { */ public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) { SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener(); - int status = captureLayers(captureArgs, syncScreenCapture); + int status = nativeCaptureLayers(captureArgs, syncScreenCapture.mNativeObject, + Flags.syncScreenCapture()); if (status != 0) { return null; } @@ -171,7 +174,7 @@ public class ScreenCapture { */ public static int captureLayers(@NonNull LayerCaptureArgs captureArgs, @NonNull ScreenCaptureListener captureListener) { - return nativeCaptureLayers(captureArgs, captureListener.mNativeObject); + return nativeCaptureLayers(captureArgs, captureListener.mNativeObject, false /* sync */); } /** @@ -674,7 +677,7 @@ public class ScreenCapture { * This listener can only be used for a single call to capture content call. */ public static class ScreenCaptureListener implements Parcelable { - private final long mNativeObject; + final long mNativeObject; private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer()); diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 751c1a8dd0ff..069affb4c06c 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -88,3 +88,11 @@ flag { is_fixed_read_only: true bug: "304574518" } + +flag { + namespace: "window_surfaces" + name: "sync_screen_capture" + description: "Create a screen capture API that blocks in SurfaceFlinger" + is_fixed_read_only: true + bug: "321263247" +} diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index 6e5807b21d3d..4a6e8d761061 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -42,6 +42,14 @@ flag { flag { namespace: "windowing_sdk" + name: "untrusted_embedding_any_app_permission" + description: "Feature flag to enable the permission to embed any app in untrusted mode." + bug: "289199433" + is_fixed_read_only: true +} + +flag { + namespace: "windowing_sdk" name: "activity_window_info_flag" description: "To dispatch ActivityWindowInfo through ClientTransaction" bug: "287582673" diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index de0f070b01a3..b4395a77c24b 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -18,7 +18,6 @@ package com.android.internal.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets; import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE; @@ -329,7 +328,8 @@ public class AccessibilityShortcutController { } private AlertDialog createShortcutWarningDialog(int userId) { - List<AccessibilityTarget> targets = getTargets(mContext, ACCESSIBILITY_SHORTCUT_KEY); + List<AccessibilityTarget> targets = getTargets(mContext, + ShortcutConstants.UserShortcutType.HARDWARE); if (targets.size() == 0) { return null; } @@ -541,7 +541,7 @@ public class AccessibilityShortcutController { private ComponentName getShortcutTargetComponentName() { final List<String> shortcutTargets = mFrameworkObjectProvider .getAccessibilityManagerInstance(mContext) - .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY); + .getAccessibilityShortcutTargets(ShortcutConstants.UserShortcutType.HARDWARE); if (shortcutTargets.size() != 1) { return null; } diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java index 7ec8838699b3..353e1826a27a 100644 --- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java +++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java @@ -44,19 +44,27 @@ public final class ShortcutConstants { * choose accessibility shortcut as preferred shortcut. * {@code TRIPLETAP} for displaying specifying magnification to be toggled via quickly * tapping screen 3 times as preferred shortcut. + * {@code TWO_FINGERS_TRIPLE_TAP} for displaying specifying magnification to be toggled via + * quickly tapping screen 3 times with two fingers as preferred shortcut. */ @Retention(RetentionPolicy.SOURCE) - @IntDef({ - UserShortcutType.DEFAULT, - UserShortcutType.SOFTWARE, - UserShortcutType.HARDWARE, - UserShortcutType.TRIPLETAP, - }) + @IntDef( + flag = true, + value = { + UserShortcutType.DEFAULT, + UserShortcutType.SOFTWARE, + UserShortcutType.HARDWARE, + UserShortcutType.TRIPLETAP, + UserShortcutType.TWO_FINGERS_TRIPLE_TAP, + }) public @interface UserShortcutType { int DEFAULT = 0; - int SOFTWARE = 1; // 1 << 0 - int HARDWARE = 2; // 1 << 1 - int TRIPLETAP = 4; // 1 << 2 + // LINT.IfChange(shortcut_type_intdef) + int SOFTWARE = 1; + int HARDWARE = 1 << 1; + int TRIPLETAP = 1 << 2; + int TWO_FINGERS_TRIPLE_TAP = 1 << 3; + // LINT.ThenChange(:shortcut_type_array) } /** @@ -64,9 +72,12 @@ public final class ShortcutConstants { * non-default IntDef types. */ public static final int[] USER_SHORTCUT_TYPES = { + // LINT.IfChange(shortcut_type_array) UserShortcutType.SOFTWARE, UserShortcutType.HARDWARE, - UserShortcutType.TRIPLETAP + UserShortcutType.TRIPLETAP, + UserShortcutType.TWO_FINGERS_TRIPLE_TAP, + // LINT.ThenChange(:shortcut_type_intdef) }; diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityActivityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityActivityTarget.java index 063154d9a6d6..33048dca12b0 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityActivityTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityActivityTarget.java @@ -17,14 +17,13 @@ package com.android.internal.accessibility.dialog; import static com.android.internal.accessibility.util.ShortcutUtils.convertToKey; -import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType; import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained; import android.accessibilityservice.AccessibilityShortcutInfo; import android.annotation.NonNull; import android.content.Context; -import android.view.accessibility.AccessibilityManager.ShortcutType; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; @@ -33,7 +32,8 @@ import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuM */ class AccessibilityActivityTarget extends AccessibilityTarget { - AccessibilityActivityTarget(Context context, @ShortcutType int shortcutType, + AccessibilityActivityTarget(Context context, + @ShortcutConstants.UserShortcutType int shortcutType, @NonNull AccessibilityShortcutInfo shortcutInfo) { super(context, shortcutType, @@ -44,7 +44,7 @@ class AccessibilityActivityTarget extends AccessibilityTarget { shortcutInfo.getActivityInfo().applicationInfo.uid, shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager()), shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager()), - convertToKey(convertToUserType(shortcutType))); + convertToKey(shortcutType)); } @Override diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java index 7eb09e59601b..e084ebdaf3a8 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java @@ -17,7 +17,6 @@ package com.android.internal.accessibility.dialog; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; @@ -36,6 +35,7 @@ import android.widget.GridView; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.widget.ResolverDrawerLayout; import java.util.ArrayList; @@ -85,7 +85,7 @@ public class AccessibilityButtonChooserActivity extends Activity { prompt.setVisibility(View.VISIBLE); } - mTargets.addAll(getTargets(this, ACCESSIBILITY_BUTTON)); + mTargets.addAll(getTargets(this, ShortcutConstants.UserShortcutType.SOFTWARE)); final GridView gridview = findViewById(R.id.accessibility_button_chooser_grid); gridview.setAdapter(new ButtonTargetAdapter(mTargets)); diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java index 2b6913ca5e5a..7406da42507f 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceTarget.java @@ -17,14 +17,13 @@ package com.android.internal.accessibility.dialog; import static com.android.internal.accessibility.util.ShortcutUtils.convertToKey; -import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType; import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.content.Context; -import android.view.accessibility.AccessibilityManager.ShortcutType; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; @@ -36,7 +35,9 @@ class AccessibilityServiceTarget extends AccessibilityTarget { private final AccessibilityServiceInfo mAccessibilityServiceInfo; - AccessibilityServiceTarget(Context context, @ShortcutType int shortcutType, + AccessibilityServiceTarget( + Context context, + @ShortcutConstants.UserShortcutType int shortcutType, @AccessibilityFragmentType int fragmentType, @NonNull AccessibilityServiceInfo serviceInfo) { super(context, @@ -48,7 +49,7 @@ class AccessibilityServiceTarget extends AccessibilityTarget { serviceInfo.getResolveInfo().serviceInfo.applicationInfo.uid, serviceInfo.getResolveInfo().loadLabel(context.getPackageManager()), serviceInfo.getResolveInfo().loadIcon(context.getPackageManager()), - convertToKey(convertToUserType(shortcutType))); + convertToKey(shortcutType)); mAccessibilityServiceInfo = serviceInfo; } diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java index 2e80b7e19516..8e2ec1bfd53d 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java @@ -15,10 +15,6 @@ */ package com.android.internal.accessibility.dialog; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; -import static android.view.accessibility.AccessibilityManager.ShortcutType; - import static com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.createEnableDialogContentView; import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getInstalledTargets; @@ -42,6 +38,7 @@ import android.view.accessibility.Flags; import android.widget.AdapterView; import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; @@ -52,8 +49,8 @@ import java.util.List; * activity or allowlisting feature for volume key shortcut. */ public class AccessibilityShortcutChooserActivity extends Activity { - @ShortcutType - private final int mShortcutType = ACCESSIBILITY_SHORTCUT_KEY; + @ShortcutConstants.UserShortcutType + private final int mShortcutType = ShortcutConstants.UserShortcutType.HARDWARE; private static final String KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE = "accessibility_shortcut_menu_mode"; private final List<AccessibilityTarget> mTargets = new ArrayList<>(); @@ -246,7 +243,7 @@ public class AccessibilityShortcutChooserActivity extends Activity { mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT; final int selectDialogTitleId = R.string.accessibility_select_shortcut_menu_title; final int editDialogTitleId = - mShortcutType == ACCESSIBILITY_BUTTON + mShortcutType == ShortcutConstants.UserShortcutType.SOFTWARE ? R.string.accessibility_edit_shortcut_menu_button_title : R.string.accessibility_edit_shortcut_menu_volume_title; diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java index 652cb5233461..4ab1ee9d6b46 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java @@ -16,10 +16,6 @@ package com.android.internal.accessibility.dialog; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; - -import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType; import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings; import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings; @@ -30,7 +26,6 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.view.View; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.ShortcutType; import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; @@ -47,7 +42,7 @@ import com.android.internal.annotations.VisibleForTesting; public abstract class AccessibilityTarget implements TargetOperations, OnTargetSelectedListener, OnTargetCheckedChangeListener { private Context mContext; - @ShortcutType + @ShortcutConstants.UserShortcutType private int mShortcutType; @AccessibilityFragmentType private int mFragmentType; @@ -61,7 +56,8 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS private CharSequence mStateDescription; @VisibleForTesting - public AccessibilityTarget(Context context, @ShortcutType int shortcutType, + public AccessibilityTarget( + Context context, @ShortcutConstants.UserShortcutType int shortcutType, @AccessibilityFragmentType int fragmentType, boolean isShortcutSwitched, String id, int uid, CharSequence label, Drawable icon, String key) { mContext = context; @@ -99,10 +95,10 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS final AccessibilityManager am = getContext().getSystemService(AccessibilityManager.class); switch (getShortcutType()) { - case ACCESSIBILITY_BUTTON: + case ShortcutConstants.UserShortcutType.SOFTWARE: am.notifyAccessibilityButtonClicked(getContext().getDisplayId(), getId()); return; - case ACCESSIBILITY_SHORTCUT_KEY: + case ShortcutConstants.UserShortcutType.HARDWARE: am.performAccessibilityShortcut(getId()); return; default: @@ -114,9 +110,9 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS public void onCheckedChanged(boolean isChecked) { setShortcutEnabled(isChecked); if (isChecked) { - optInValueToSettings(getContext(), convertToUserType(getShortcutType()), getId()); + optInValueToSettings(getContext(), getShortcutType(), getId()); } else { - optOutValueFromSettings(getContext(), convertToUserType(getShortcutType()), getId()); + optOutValueFromSettings(getContext(), getShortcutType(), getId()); } } @@ -142,7 +138,7 @@ public abstract class AccessibilityTarget implements TargetOperations, OnTargetS return mContext; } - public @ShortcutType int getShortcutType() { + public @ShortcutConstants.UserShortcutType int getShortcutType() { return mShortcutType; } diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java index 51a5ddfa8dd6..bd63e23a066f 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java @@ -16,8 +16,6 @@ package com.android.internal.accessibility.dialog; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; - import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; @@ -41,12 +39,12 @@ import android.text.BidiFormatter; import android.view.LayoutInflater; import android.view.View; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.ShortcutType; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; import java.util.ArrayList; @@ -70,8 +68,9 @@ public final class AccessibilityTargetHelper { * @return The list of {@link AccessibilityTarget}. * @hide */ - public static List<AccessibilityTarget> getTargets(Context context, - @ShortcutType int shortcutType) { + public static List<AccessibilityTarget> getTargets( + Context context, + @ShortcutConstants.UserShortcutType int shortcutType) { // List all accessibility target final List<AccessibilityTarget> installedTargets = getInstalledTargets(context, shortcutType); @@ -113,7 +112,7 @@ public final class AccessibilityTargetHelper { * @return The list of {@link AccessibilityTarget}. */ static List<AccessibilityTarget> getInstalledTargets(Context context, - @ShortcutType int shortcutType) { + @ShortcutConstants.UserShortcutType int shortcutType) { final List<AccessibilityTarget> targets = new ArrayList<>(); targets.addAll(getAccessibilityFilteredTargets(context, shortcutType)); targets.addAll(getAllowListingFeatureTargets(context, shortcutType)); @@ -122,7 +121,7 @@ public final class AccessibilityTargetHelper { } private static List<AccessibilityTarget> getAccessibilityFilteredTargets(Context context, - @ShortcutType int shortcutType) { + @ShortcutConstants.UserShortcutType int shortcutType) { final List<AccessibilityTarget> serviceTargets = getAccessibilityServiceTargets(context, shortcutType); final List<AccessibilityTarget> activityTargets = @@ -155,7 +154,7 @@ public final class AccessibilityTargetHelper { } private static List<AccessibilityTarget> getAccessibilityServiceTargets(Context context, - @ShortcutType int shortcutType) { + @ShortcutConstants.UserShortcutType int shortcutType) { final AccessibilityManager am = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); final List<AccessibilityServiceInfo> installedServices = @@ -171,7 +170,7 @@ public final class AccessibilityTargetHelper { final boolean hasRequestAccessibilityButtonFlag = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; if ((targetSdk <= Build.VERSION_CODES.Q) && !hasRequestAccessibilityButtonFlag - && (shortcutType == ACCESSIBILITY_BUTTON)) { + && (shortcutType == ShortcutConstants.UserShortcutType.SOFTWARE)) { continue; } @@ -182,7 +181,7 @@ public final class AccessibilityTargetHelper { } private static List<AccessibilityTarget> getAccessibilityActivityTargets(Context context, - @ShortcutType int shortcutType) { + @ShortcutConstants.UserShortcutType int shortcutType) { final AccessibilityManager am = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); final List<AccessibilityShortcutInfo> installedServices = @@ -201,7 +200,7 @@ public final class AccessibilityTargetHelper { } private static List<AccessibilityTarget> getAllowListingFeatureTargets(Context context, - @ShortcutType int shortcutType) { + @ShortcutConstants.UserShortcutType int shortcutType) { final List<AccessibilityTarget> targets = new ArrayList<>(); final int uid = context.getApplicationInfo().uid; @@ -281,8 +280,10 @@ public final class AccessibilityTargetHelper { return targets; } - private static AccessibilityTarget createAccessibilityServiceTarget(Context context, - @ShortcutType int shortcutType, @NonNull AccessibilityServiceInfo info) { + private static AccessibilityTarget createAccessibilityServiceTarget( + Context context, + @ShortcutConstants.UserShortcutType int shortcutType, + @NonNull AccessibilityServiceInfo info) { switch (getAccessibilityServiceFragmentType(info)) { case AccessibilityFragmentType.VOLUME_SHORTCUT_TOGGLE: return new VolumeShortcutToggleAccessibilityServiceTarget(context, shortcutType, diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java index 1bc8b84e6869..641a9f18e3d6 100644 --- a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java @@ -16,9 +16,6 @@ package com.android.internal.accessibility.dialog; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; - import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState; import static com.android.internal.accessibility.util.ShortcutUtils.isComponentIdExistingInSettings; @@ -28,7 +25,6 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.os.UserHandle; -import android.view.accessibility.AccessibilityManager.ShortcutType; import android.view.accessibility.Flags; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; @@ -45,7 +41,7 @@ import java.util.Set; public class InvisibleToggleAccessibilityServiceTarget extends AccessibilityServiceTarget { public InvisibleToggleAccessibilityServiceTarget( - Context context, @ShortcutType int shortcutType, + Context context, @UserShortcutType int shortcutType, @NonNull AccessibilityServiceInfo serviceInfo) { super(context, shortcutType, @@ -72,10 +68,10 @@ public class InvisibleToggleAccessibilityServiceTarget extends AccessibilityServ private boolean isComponentIdExistingInOtherShortcut() { switch (getShortcutType()) { - case ACCESSIBILITY_BUTTON: + case UserShortcutType.SOFTWARE: return isComponentIdExistingInSettings(getContext(), UserShortcutType.HARDWARE, getId()); - case ACCESSIBILITY_SHORTCUT_KEY: + case UserShortcutType.HARDWARE: return isComponentIdExistingInSettings(getContext(), UserShortcutType.SOFTWARE, getId()); default: diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java index c22f17dfa967..2204c0beb4fd 100644 --- a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java @@ -18,8 +18,8 @@ package com.android.internal.accessibility.dialog; import android.content.Context; import android.graphics.drawable.Drawable; -import android.view.accessibility.AccessibilityManager.ShortcutType; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; /** @@ -28,7 +28,8 @@ import com.android.internal.accessibility.common.ShortcutConstants.Accessibility */ class InvisibleToggleAllowListingFeatureTarget extends AccessibilityTarget { - InvisibleToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType, + InvisibleToggleAllowListingFeatureTarget(Context context, + @ShortcutConstants.UserShortcutType int shortcutType, boolean isShortcutSwitched, String id, int uid, CharSequence label, Drawable icon, String key) { super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE, isShortcutSwitched, diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java index a4ffef6bfbc2..a6ef73e90772 100644 --- a/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/ToggleAccessibilityServiceTarget.java @@ -22,9 +22,9 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.content.Context; import android.view.View; -import android.view.accessibility.AccessibilityManager.ShortcutType; import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder; @@ -45,7 +45,8 @@ class ToggleAccessibilityServiceTarget extends AccessibilityServiceTarget { float DISABLED = 0.5f; } - ToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType, + ToggleAccessibilityServiceTarget(Context context, + @ShortcutConstants.UserShortcutType int shortcutType, @NonNull AccessibilityServiceInfo serviceInfo) { super(context, shortcutType, diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java index 11e668f51774..2a9c555efce8 100644 --- a/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java @@ -21,9 +21,9 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.provider.Settings; import android.view.View; -import android.view.accessibility.AccessibilityManager.ShortcutType; import com.android.internal.R; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType; import com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder; @@ -34,7 +34,8 @@ import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder; */ class ToggleAllowListingFeatureTarget extends AccessibilityTarget { - ToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType, + ToggleAllowListingFeatureTarget(Context context, + @ShortcutConstants.UserShortcutType int shortcutType, boolean isShortcutSwitched, String id, int uid, CharSequence label, Drawable icon, String key) { super(context, shortcutType, AccessibilityFragmentType.TOGGLE, isShortcutSwitched, id, diff --git a/core/java/com/android/internal/accessibility/dialog/VolumeShortcutToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/VolumeShortcutToggleAccessibilityServiceTarget.java index 04f5061fbd8e..4926e7218530 100644 --- a/core/java/com/android/internal/accessibility/dialog/VolumeShortcutToggleAccessibilityServiceTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/VolumeShortcutToggleAccessibilityServiceTarget.java @@ -16,9 +16,6 @@ package com.android.internal.accessibility.dialog; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; - import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState; import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings; @@ -27,7 +24,6 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; -import android.view.accessibility.AccessibilityManager.ShortcutType; import android.widget.Toast; import com.android.internal.R; @@ -39,7 +35,8 @@ import com.android.internal.accessibility.common.ShortcutConstants.Accessibility */ class VolumeShortcutToggleAccessibilityServiceTarget extends AccessibilityServiceTarget { - VolumeShortcutToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType, + VolumeShortcutToggleAccessibilityServiceTarget(Context context, + @UserShortcutType int shortcutType, @NonNull AccessibilityServiceInfo serviceInfo) { super(context, shortcutType, @@ -50,10 +47,10 @@ class VolumeShortcutToggleAccessibilityServiceTarget extends AccessibilityServic @Override public void onCheckedChanged(boolean isChecked) { switch (getShortcutType()) { - case ACCESSIBILITY_BUTTON: + case UserShortcutType.SOFTWARE: onCheckedFromAccessibilityButton(isChecked); return; - case ACCESSIBILITY_SHORTCUT_KEY: + case UserShortcutType.HARDWARE: super.onCheckedChanged(isChecked); return; default: diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java index 6b074a610818..1e4bcf21c635 100644 --- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java +++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java @@ -21,8 +21,6 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED; @@ -47,9 +45,8 @@ import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_ import android.content.ComponentName; import android.content.Context; import android.provider.Settings; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.ShortcutType; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.util.FrameworkStatsLog; /** Methods for logging accessibility states. */ @@ -71,15 +68,15 @@ public final class AccessibilityStatsLogUtils { /** * Logs accessibility feature name that is assigned to the given {@code shortcutType}. - * Calls this when clicking the shortcut {@link AccessibilityManager#ACCESSIBILITY_BUTTON} or - * {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}. + * Calls this when clicking the shortcut {@link ShortcutConstants.UserShortcutType.SOFTWARE} or + * {@link ShortcutConstants.UserShortcutType.HARDWARE}. * * @param context context used to retrieve the {@link Settings} provider * @param componentName component name of the accessibility feature * @param shortcutType accessibility shortcut type */ public static void logAccessibilityShortcutActivated(Context context, - ComponentName componentName, @ShortcutType int shortcutType) { + ComponentName componentName, @ShortcutConstants.UserShortcutType int shortcutType) { logAccessibilityShortcutActivatedInternal(componentName, convertToLoggingShortcutType(context, shortcutType), UNKNOWN_STATUS); } @@ -87,8 +84,8 @@ public final class AccessibilityStatsLogUtils { /** * Logs accessibility feature name that is assigned to the given {@code shortcutType} and the * {@code serviceEnabled} status. - * Calls this when clicking the shortcut {@link AccessibilityManager#ACCESSIBILITY_BUTTON} - * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}. + * Calls this when clicking the shortcut {@link ShortcutConstants.UserShortcutType.SOFTWARE} + * or {@link ShortcutConstants.UserShortcutType.HARDWARE}. * * @param context context used to retrieve the {@link Settings} provider * @param componentName component name of the accessibility feature @@ -96,7 +93,8 @@ public final class AccessibilityStatsLogUtils { * @param serviceEnabled {@code true} if the service is enabled */ public static void logAccessibilityShortcutActivated(Context context, - ComponentName componentName, @ShortcutType int shortcutType, boolean serviceEnabled) { + ComponentName componentName, @ShortcutConstants.UserShortcutType int shortcutType, + boolean serviceEnabled) { logAccessibilityShortcutActivatedInternal(componentName, convertToLoggingShortcutType(context, shortcutType), convertToLoggingServiceStatus(serviceEnabled)); @@ -235,9 +233,9 @@ public final class AccessibilityStatsLogUtils { } private static int convertToLoggingShortcutType(Context context, - @ShortcutType int shortcutType) { + @ShortcutConstants.UserShortcutType int shortcutType) { switch (shortcutType) { - case ACCESSIBILITY_BUTTON: + case ShortcutConstants.UserShortcutType.SOFTWARE: if (isAccessibilityFloatingMenuEnabled(context)) { return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU; } else if (isAccessibilityGestureEnabled(context)) { @@ -245,7 +243,7 @@ public final class AccessibilityStatsLogUtils { } else { return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; } - case ACCESSIBILITY_SHORTCUT_KEY: + case ShortcutConstants.UserShortcutType.HARDWARE: return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; } return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java index 3fd303038c57..276c5c4ffdea 100644 --- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java +++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java @@ -16,9 +16,6 @@ package com.android.internal.accessibility.util; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; - import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType.INVISIBLE_TOGGLE; import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR; @@ -33,7 +30,6 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.ShortcutType; import java.util.Collections; import java.util.List; @@ -144,7 +140,7 @@ public final class ShortcutUtils { * @param componentId The component id that need to be checked. * @return {@code true} if a component id is contained. */ - public static boolean isShortcutContained(Context context, @ShortcutType int shortcutType, + public static boolean isShortcutContained(Context context, @UserShortcutType int shortcutType, @NonNull String componentId) { final AccessibilityManager am = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); @@ -166,6 +162,8 @@ public final class ShortcutUtils { return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; case UserShortcutType.TRIPLETAP: return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED; + case UserShortcutType.TWO_FINGERS_TRIPLE_TAP: + return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED; default: throw new IllegalArgumentException( "Unsupported user shortcut type: " + type); @@ -173,24 +171,6 @@ public final class ShortcutUtils { } /** - * Converts {@link ShortcutType} to {@link UserShortcutType}. - * - * @param type The shortcut type. - * @return Mapping type from {@link UserShortcutType}. - */ - public static @UserShortcutType int convertToUserType(@ShortcutType int type) { - switch (type) { - case ACCESSIBILITY_BUTTON: - return UserShortcutType.SOFTWARE; - case ACCESSIBILITY_SHORTCUT_KEY: - return UserShortcutType.HARDWARE; - default: - throw new IllegalArgumentException( - "Unsupported shortcut type:" + type); - } - } - - /** * Updates an accessibility state if the accessibility service is a Always-On a11y service, * a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON * <p> @@ -255,12 +235,13 @@ public final class ShortcutUtils { public static Set<String> getShortcutTargetsFromSettings( Context context, @UserShortcutType int shortcutType, int userId) { final String targetKey = convertToKey(shortcutType); - if (Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED.equals(targetKey)) { + if (Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED.equals(targetKey) + || Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED + .equals(targetKey)) { boolean magnificationEnabled = Settings.Secure.getIntForUser( context.getContentResolver(), targetKey, /* def= */ 0, userId) == 1; return magnificationEnabled ? Set.of(MAGNIFICATION_CONTROLLER_NAME) : Collections.emptySet(); - } else { final String targetString = Settings.Secure.getStringForUser( context.getContentResolver(), targetKey, userId); diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 7a79e0f7cfea..aa60cc9e672c 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -490,7 +490,7 @@ public class BatteryStatsHistory { * Returns true if this instance only supports reading history. */ public boolean isReadOnly() { - return mActiveFile == null || mHistoryDir == null; + return !mMutable || mActiveFile == null || mHistoryDir == null; } /** @@ -508,6 +508,13 @@ public class BatteryStatsHistory { * create next history file. */ public void startNextFile(long elapsedRealtimeMs) { + synchronized (this) { + startNextFileLocked(elapsedRealtimeMs); + } + } + + @GuardedBy("this") + private void startNextFileLocked(long elapsedRealtimeMs) { if (mMaxHistoryFiles == 0) { Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history"); return; @@ -548,10 +555,7 @@ public class BatteryStatsHistory { } mWrittenPowerStatsDescriptors.clear(); - - synchronized (this) { - cleanupLocked(); - } + cleanupLocked(); } @GuardedBy("this") @@ -599,27 +603,31 @@ public class BatteryStatsHistory { * number 0 again. */ public void reset() { - if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!"); - for (BatteryHistoryFile file : mHistoryFiles) { - file.atomicFile.delete(); - } - mHistoryFiles.clear(); + synchronized (this) { + if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!"); + for (BatteryHistoryFile file : mHistoryFiles) { + file.atomicFile.delete(); + } + mHistoryFiles.clear(); - BatteryHistoryFile name = makeBatteryHistoryFile(); - mHistoryFiles.add(name); - setActiveFile(name); + BatteryHistoryFile name = makeBatteryHistoryFile(); + mHistoryFiles.add(name); + setActiveFile(name); - initHistoryBuffer(); + initHistoryBuffer(); + } } /** * Returns the monotonic clock time when the available battery history collection started. */ public long getStartTime() { - if (!mHistoryFiles.isEmpty()) { - return mHistoryFiles.get(0).monotonicTimeMs; - } else { - return mHistoryBufferStartTime; + synchronized (this) { + if (!mHistoryFiles.isEmpty()) { + return mHistoryFiles.get(0).monotonicTimeMs; + } else { + return mHistoryBufferStartTime; + } } } @@ -633,11 +641,14 @@ public class BatteryStatsHistory { */ @NonNull public BatteryStatsHistoryIterator iterate(long startTimeMs, long endTimeMs) { + if (mMutable) { + return copy().iterate(startTimeMs, endTimeMs); + } + mCurrentFileIndex = 0; mCurrentParcel = null; mCurrentParcelEnd = 0; mParcelIndex = 0; - mMutable = false; if (mWritableHistory != null) { synchronized (mWritableHistory) { mWritableHistory.setCleanupEnabledLocked(false); @@ -650,14 +661,11 @@ public class BatteryStatsHistory { * Finish iterating history files and history buffer. */ void iteratorFinished() { - // setDataPosition so mHistoryBuffer Parcel can be written. mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize()); if (mWritableHistory != null) { synchronized (mWritableHistory) { mWritableHistory.setCleanupEnabledLocked(true); } - } else { - mMutable = true; } } @@ -671,6 +679,8 @@ public class BatteryStatsHistory { */ @Nullable public Parcel getNextParcel(long startTimeMs, long endTimeMs) { + checkImmutable(); + // First iterate through all records in current parcel. if (mCurrentParcel != null) { if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) { @@ -754,6 +764,12 @@ public class BatteryStatsHistory { return mCurrentParcel; } + private void checkImmutable() { + if (mMutable) { + throw new IllegalStateException("Iterating over a mutable battery history"); + } + } + /** * Read history file into a parcel. * @@ -863,8 +879,10 @@ public class BatteryStatsHistory { * @param out the output parcel */ public void writeToParcel(Parcel out) { - writeHistoryBuffer(out); - writeToParcel(out, false /* useBlobs */); + synchronized (this) { + writeHistoryBuffer(out); + writeToParcel(out, false /* useBlobs */); + } } /** @@ -874,8 +892,10 @@ public class BatteryStatsHistory { * @param out the output parcel */ public void writeToBatteryUsageStatsParcel(Parcel out) { - out.writeBlob(mHistoryBuffer.marshall()); - writeToParcel(out, true /* useBlobs */); + synchronized (this) { + out.writeBlob(mHistoryBuffer.marshall()); + writeToParcel(out, true /* useBlobs */); + } } private void writeToParcel(Parcel out, boolean useBlobs) { @@ -1022,14 +1042,18 @@ public class BatteryStatsHistory { * Enables/disables recording of history. When disabled, all "record*" calls are a no-op. */ public void setHistoryRecordingEnabled(boolean enabled) { - mRecordingHistory = enabled; + synchronized (this) { + mRecordingHistory = enabled; + } } /** * Returns true if history recording is enabled. */ public boolean isRecordingHistory() { - return mRecordingHistory; + synchronized (this) { + return mRecordingHistory; + } } /** @@ -1037,8 +1061,10 @@ public class BatteryStatsHistory { */ @VisibleForTesting public void forceRecordAllHistory() { - mHaveBatteryLevel = true; - mRecordingHistory = true; + synchronized (this) { + mHaveBatteryLevel = true; + mRecordingHistory = true; + } } /** @@ -1046,37 +1072,43 @@ public class BatteryStatsHistory { */ public void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs, boolean reset) { - mRecordingHistory = true; - mHistoryCur.currentTime = mClock.currentTimeMillis(); - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, - reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME); - mHistoryCur.currentTime = 0; + synchronized (this) { + mRecordingHistory = true; + mHistoryCur.currentTime = mClock.currentTimeMillis(); + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, + reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME); + mHistoryCur.currentTime = 0; + } } /** * Prepares to continue recording after restoring previous history from persistent storage. */ public void continueRecordingHistory() { - if (mHistoryBuffer.dataPosition() <= 0 && mHistoryFiles.size() <= 1) { - return; - } + synchronized (this) { + if (mHistoryBuffer.dataPosition() <= 0 && mHistoryFiles.size() <= 1) { + return; + } - mRecordingHistory = true; - final long elapsedRealtimeMs = mClock.elapsedRealtime(); - final long uptimeMs = mClock.uptimeMillis(); - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_START); - startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); + mRecordingHistory = true; + final long elapsedRealtimeMs = mClock.elapsedRealtime(); + final long uptimeMs = mClock.uptimeMillis(); + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_START); + startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); + } } /** * Notes the current battery state to be reflected in the next written history item. */ public void setBatteryState(boolean charging, int status, int level, int chargeUah) { - mHaveBatteryLevel = true; - setChargingState(charging); - mHistoryCur.batteryStatus = (byte) status; - mHistoryCur.batteryLevel = (byte) level; - mHistoryCur.batteryChargeUah = chargeUah; + synchronized (this) { + mHaveBatteryLevel = true; + setChargingState(charging); + mHistoryCur.batteryStatus = (byte) status; + mHistoryCur.batteryLevel = (byte) level; + mHistoryCur.batteryChargeUah = chargeUah; + } } /** @@ -1084,24 +1116,28 @@ public class BatteryStatsHistory { */ public void setBatteryState(int status, int level, int health, int plugType, int temperature, int voltageMv, int chargeUah) { - mHaveBatteryLevel = true; - mHistoryCur.batteryStatus = (byte) status; - mHistoryCur.batteryLevel = (byte) level; - mHistoryCur.batteryHealth = (byte) health; - mHistoryCur.batteryPlugType = (byte) plugType; - mHistoryCur.batteryTemperature = (short) temperature; - mHistoryCur.batteryVoltage = (char) voltageMv; - mHistoryCur.batteryChargeUah = chargeUah; + synchronized (this) { + mHaveBatteryLevel = true; + mHistoryCur.batteryStatus = (byte) status; + mHistoryCur.batteryLevel = (byte) level; + mHistoryCur.batteryHealth = (byte) health; + mHistoryCur.batteryPlugType = (byte) plugType; + mHistoryCur.batteryTemperature = (short) temperature; + mHistoryCur.batteryVoltage = (char) voltageMv; + mHistoryCur.batteryChargeUah = chargeUah; + } } /** * Notes the current power plugged-in state to be reflected in the next written history item. */ public void setPluggedInState(boolean pluggedIn) { - if (pluggedIn) { - mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; - } else { - mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; + synchronized (this) { + if (pluggedIn) { + mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; + } else { + mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; + } } } @@ -1109,10 +1145,12 @@ public class BatteryStatsHistory { * Notes the current battery charging state to be reflected in the next written history item. */ public void setChargingState(boolean charging) { - if (charging) { - mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG; - } else { - mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG; + synchronized (this) { + if (charging) { + mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG; + } else { + mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG; + } } } @@ -1121,38 +1159,44 @@ public class BatteryStatsHistory { */ public void recordEvent(long elapsedRealtimeMs, long uptimeMs, int code, String name, int uid) { - mHistoryCur.eventCode = code; - mHistoryCur.eventTag = mHistoryCur.localEventTag; - mHistoryCur.eventTag.string = name; - mHistoryCur.eventTag.uid = uid; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.eventCode = code; + mHistoryCur.eventTag = mHistoryCur.localEventTag; + mHistoryCur.eventTag.string = name; + mHistoryCur.eventTag.uid = uid; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records a time change event. */ public void recordCurrentTimeChange(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) { - if (!mRecordingHistory) { - return; - } + synchronized (this) { + if (!mRecordingHistory) { + return; + } - mHistoryCur.currentTime = currentTimeMs; - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, - HistoryItem.CMD_CURRENT_TIME); - mHistoryCur.currentTime = 0; + mHistoryCur.currentTime = currentTimeMs; + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, + HistoryItem.CMD_CURRENT_TIME); + mHistoryCur.currentTime = 0; + } } /** * Records a system shutdown event. */ public void recordShutdownEvent(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) { - if (!mRecordingHistory) { - return; - } + synchronized (this) { + if (!mRecordingHistory) { + return; + } - mHistoryCur.currentTime = currentTimeMs; - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_SHUTDOWN); - mHistoryCur.currentTime = 0; + mHistoryCur.currentTime = currentTimeMs; + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_SHUTDOWN); + mHistoryCur.currentTime = 0; + } } /** @@ -1160,13 +1204,15 @@ public class BatteryStatsHistory { */ public void recordBatteryState(long elapsedRealtimeMs, long uptimeMs, int batteryLevel, boolean isPlugged) { - mHistoryCur.batteryLevel = (byte) batteryLevel; - setPluggedInState(isPlugged); - if (DEBUG) { - Slog.v(TAG, "Battery unplugged to: " - + Integer.toHexString(mHistoryCur.states)); + synchronized (this) { + mHistoryCur.batteryLevel = (byte) batteryLevel; + setPluggedInState(isPlugged); + if (DEBUG) { + Slog.v(TAG, "Battery unplugged to: " + + Integer.toHexString(mHistoryCur.states)); + } + writeHistoryItem(elapsedRealtimeMs, uptimeMs); } - writeHistoryItem(elapsedRealtimeMs, uptimeMs); } /** @@ -1174,9 +1220,11 @@ public class BatteryStatsHistory { */ public void recordPowerStats(long elapsedRealtimeMs, long uptimeMs, PowerStats powerStats) { - mHistoryCur.powerStats = powerStats; - mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.powerStats = powerStats; + mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1184,11 +1232,13 @@ public class BatteryStatsHistory { */ public void recordProcessStateChange(long elapsedRealtimeMs, long uptimeMs, int uid, @BatteryConsumer.ProcessState int processState) { - mHistoryCur.processStateChange = mHistoryCur.localProcessStateChange; - mHistoryCur.processStateChange.uid = uid; - mHistoryCur.processStateChange.processState = processState; - mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.processStateChange = mHistoryCur.localProcessStateChange; + mHistoryCur.processStateChange.uid = uid; + mHistoryCur.processStateChange.processState = processState; + mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1197,8 +1247,10 @@ public class BatteryStatsHistory { */ public void recordWifiConsumedCharge(long elapsedRealtimeMs, long uptimeMs, double monitoredRailChargeMah) { - mHistoryCur.wifiRailChargeMah += monitoredRailChargeMah; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.wifiRailChargeMah += monitoredRailChargeMah; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1206,10 +1258,12 @@ public class BatteryStatsHistory { */ public void recordWakelockStartEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid) { - mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName; - mHistoryCur.wakelockTag.uid = uid; - recordStateStartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + synchronized (this) { + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = historyName; + mHistoryCur.wakelockTag.uid = uid; + recordStateStartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + } } /** @@ -1217,18 +1271,20 @@ public class BatteryStatsHistory { */ public boolean maybeUpdateWakelockTag(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid) { - if (mHistoryLastWritten.cmd != HistoryItem.CMD_UPDATE) { - return false; - } - if (mHistoryLastWritten.wakelockTag != null) { - // We'll try to update the last tag. - mHistoryLastWritten.wakelockTag = null; - mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName; - mHistoryCur.wakelockTag.uid = uid; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + if (mHistoryLastWritten.cmd != HistoryItem.CMD_UPDATE) { + return false; + } + if (mHistoryLastWritten.wakelockTag != null) { + // We'll try to update the last tag. + mHistoryLastWritten.wakelockTag = null; + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = historyName; + mHistoryCur.wakelockTag.uid = uid; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } + return true; } - return true; } /** @@ -1236,26 +1292,32 @@ public class BatteryStatsHistory { */ public void recordWakelockStopEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid) { - mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName != null ? historyName : ""; - mHistoryCur.wakelockTag.uid = uid; - recordStateStopEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + synchronized (this) { + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = historyName != null ? historyName : ""; + mHistoryCur.wakelockTag.uid = uid; + recordStateStopEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + } } /** * Records an event when some state flag changes to true. */ public void recordStateStartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states |= stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states |= stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an event when some state flag changes to false. */ public void recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states &= ~stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states &= ~stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1263,34 +1325,42 @@ public class BatteryStatsHistory { */ public void recordStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int stateStartFlags, int stateStopFlags) { - mHistoryCur.states = (mHistoryCur.states | stateStartFlags) & ~stateStopFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states = (mHistoryCur.states | stateStartFlags) & ~stateStopFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an event when some state2 flag changes to true. */ public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states2 |= stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 |= stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an event when some state2 flag changes to false. */ public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states2 &= ~stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 &= ~stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an wakeup event. */ public void recordWakeupEvent(long elapsedRealtimeMs, long uptimeMs, String reason) { - mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag; - mHistoryCur.wakeReasonTag.string = reason; - mHistoryCur.wakeReasonTag.uid = 0; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag; + mHistoryCur.wakeReasonTag.string = reason; + mHistoryCur.wakeReasonTag.uid = 0; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1298,10 +1368,12 @@ public class BatteryStatsHistory { */ public void recordScreenBrightnessEvent(long elapsedRealtimeMs, long uptimeMs, int brightnessBin) { - mHistoryCur.states = setBitField(mHistoryCur.states, brightnessBin, - HistoryItem.STATE_BRIGHTNESS_SHIFT, - HistoryItem.STATE_BRIGHTNESS_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states = setBitField(mHistoryCur.states, brightnessBin, + HistoryItem.STATE_BRIGHTNESS_SHIFT, + HistoryItem.STATE_BRIGHTNESS_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1309,20 +1381,24 @@ public class BatteryStatsHistory { */ public void recordGpsSignalQualityEvent(long elapsedRealtimeMs, long uptimeMs, int signalLevel) { - mHistoryCur.states2 = setBitField(mHistoryCur.states2, signalLevel, - HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, - HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = setBitField(mHistoryCur.states2, signalLevel, + HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, + HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records a device idle mode change event. */ public void recordDeviceIdleEvent(long elapsedRealtimeMs, long uptimeMs, int mode) { - mHistoryCur.states2 = setBitField(mHistoryCur.states2, mode, - HistoryItem.STATE2_DEVICE_IDLE_SHIFT, - HistoryItem.STATE2_DEVICE_IDLE_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = setBitField(mHistoryCur.states2, mode, + HistoryItem.STATE2_DEVICE_IDLE_SHIFT, + HistoryItem.STATE2_DEVICE_IDLE_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1330,20 +1406,22 @@ public class BatteryStatsHistory { */ public void recordPhoneStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int addStateFlag, int removeStateFlag, int state, int signalStrength) { - mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag; - if (state != -1) { - mHistoryCur.states = - setBitField(mHistoryCur.states, state, - HistoryItem.STATE_PHONE_STATE_SHIFT, - HistoryItem.STATE_PHONE_STATE_MASK); - } - if (signalStrength != -1) { - mHistoryCur.states = - setBitField(mHistoryCur.states, signalStrength, - HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, - HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK); + synchronized (this) { + mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag; + if (state != -1) { + mHistoryCur.states = + setBitField(mHistoryCur.states, state, + HistoryItem.STATE_PHONE_STATE_SHIFT, + HistoryItem.STATE_PHONE_STATE_MASK); + } + if (signalStrength != -1) { + mHistoryCur.states = + setBitField(mHistoryCur.states, signalStrength, + HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, + HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK); + } + writeHistoryItem(elapsedRealtimeMs, uptimeMs); } - writeHistoryItem(elapsedRealtimeMs, uptimeMs); } /** @@ -1351,10 +1429,12 @@ public class BatteryStatsHistory { */ public void recordDataConnectionTypeChangeEvent(long elapsedRealtimeMs, long uptimeMs, int dataConnectionType) { - mHistoryCur.states = setBitField(mHistoryCur.states, dataConnectionType, - HistoryItem.STATE_DATA_CONNECTION_SHIFT, - HistoryItem.STATE_DATA_CONNECTION_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states = setBitField(mHistoryCur.states, dataConnectionType, + HistoryItem.STATE_DATA_CONNECTION_SHIFT, + HistoryItem.STATE_DATA_CONNECTION_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1362,10 +1442,12 @@ public class BatteryStatsHistory { */ public void recordNrStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int nrState) { - mHistoryCur.states2 = setBitField(mHistoryCur.states2, nrState, - HistoryItem.STATE2_NR_STATE_SHIFT, - HistoryItem.STATE2_NR_STATE_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = setBitField(mHistoryCur.states2, nrState, + HistoryItem.STATE2_NR_STATE_SHIFT, + HistoryItem.STATE2_NR_STATE_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1373,11 +1455,13 @@ public class BatteryStatsHistory { */ public void recordWifiSupplicantStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int supplState) { - mHistoryCur.states2 = - setBitField(mHistoryCur.states2, supplState, - HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, - HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = + setBitField(mHistoryCur.states2, supplState, + HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, + HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1385,11 +1469,13 @@ public class BatteryStatsHistory { */ public void recordWifiSignalStrengthChangeEvent(long elapsedRealtimeMs, long uptimeMs, int strengthBin) { - mHistoryCur.states2 = - setBitField(mHistoryCur.states2, strengthBin, - HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, - HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = + setBitField(mHistoryCur.states2, strengthBin, + HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, + HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1446,25 +1532,30 @@ public class BatteryStatsHistory { * Writes the current history item to history. */ public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) { - if (mTrackRunningHistoryElapsedRealtimeMs != 0) { - final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs; - final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs; - if (diffUptimeMs < (diffElapsedMs - 20)) { - final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs); - mHistoryAddTmp.setTo(mHistoryLastWritten); - mHistoryAddTmp.wakelockTag = null; - mHistoryAddTmp.wakeReasonTag = null; - mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE; - mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG; - writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp); + synchronized (this) { + if (mTrackRunningHistoryElapsedRealtimeMs != 0) { + final long diffElapsedMs = + elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs; + final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs; + if (diffUptimeMs < (diffElapsedMs - 20)) { + final long wakeElapsedTimeMs = + elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs); + mHistoryAddTmp.setTo(mHistoryLastWritten); + mHistoryAddTmp.wakelockTag = null; + mHistoryAddTmp.wakeReasonTag = null; + mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE; + mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG; + writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp); + } } + mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG; + mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs; + mTrackRunningHistoryUptimeMs = uptimeMs; + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur); } - mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG; - mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs; - mTrackRunningHistoryUptimeMs = uptimeMs; - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur); } + @GuardedBy("this") private void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { if (mTracer != null && mTracer.tracingEnabled()) { recordTraceEvents(cur.eventCode, cur.eventTag); @@ -1591,6 +1682,7 @@ public class BatteryStatsHistory { writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE); } + @GuardedBy("this") private void writeHistoryItem(long elapsedRealtimeMs, @SuppressWarnings("UnusedVariable") long uptimeMs, HistoryItem cur, byte cmd) { if (!mMutable) { @@ -1701,7 +1793,8 @@ public class BatteryStatsHistory { /** * Writes the delta between the previous and current history items into history buffer. */ - public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) { + @GuardedBy("this") + private void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) { if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) { dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS); cur.writeToParcel(dest, 0); @@ -1921,6 +2014,7 @@ public class BatteryStatsHistory { * while writing the current history buffer, the method returns * <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code> */ + @GuardedBy("this") private int writeHistoryTag(HistoryTag tag) { if (tag.string == null) { Slog.wtfStack(TAG, "writeHistoryTag called with null name"); @@ -1964,33 +2058,37 @@ public class BatteryStatsHistory { * Don't allow any more batching in to the current history event. */ public void commitCurrentHistoryBatchLocked() { - mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; + synchronized (this) { + mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; + } } /** * Saves the accumulated history buffer in the active file, see {@link #getActiveFile()} . */ public void writeHistory() { - if (isReadOnly()) { - Slog.w(TAG, "writeHistory: this instance instance is read-only"); - return; - } + synchronized (this) { + if (isReadOnly()) { + Slog.w(TAG, "writeHistory: this instance instance is read-only"); + return; + } - // Save the monotonic time first, so that even if the history write below fails, - // we still wouldn't end up with overlapping history timelines. - mMonotonicClock.write(); + // Save the monotonic time first, so that even if the history write below fails, + // we still wouldn't end up with overlapping history timelines. + mMonotonicClock.write(); - Parcel p = Parcel.obtain(); - try { - final long start = SystemClock.uptimeMillis(); - writeHistoryBuffer(p); - if (DEBUG) { - Slog.d(TAG, "writeHistoryBuffer duration ms:" - + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize()); + Parcel p = Parcel.obtain(); + try { + final long start = SystemClock.uptimeMillis(); + writeHistoryBuffer(p); + if (DEBUG) { + Slog.d(TAG, "writeHistoryBuffer duration ms:" + + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize()); + } + writeParcelToFileLocked(p, mActiveFile); + } finally { + p.recycle(); } - writeParcelToFileLocked(p, mActiveFile); - } finally { - p.recycle(); } } @@ -1998,35 +2096,38 @@ public class BatteryStatsHistory { * Reads history buffer from a persisted Parcel. */ public void readHistoryBuffer(Parcel in) throws ParcelFormatException { - final int version = in.readInt(); - if (version != BatteryStatsHistory.VERSION) { - Slog.w("BatteryStats", "readHistoryBuffer: version got " + version - + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats"); - return; - } - - mHistoryBufferStartTime = in.readLong(); - mHistoryBuffer.setDataSize(0); - mHistoryBuffer.setDataPosition(0); + synchronized (this) { + final int version = in.readInt(); + if (version != BatteryStatsHistory.VERSION) { + Slog.w("BatteryStats", "readHistoryBuffer: version got " + version + + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats"); + return; + } - int bufSize = in.readInt(); - int curPos = in.dataPosition(); - if (bufSize >= (mMaxHistoryBufferSize * 100)) { - throw new ParcelFormatException( - "File corrupt: history data buffer too large " + bufSize); - } else if ((bufSize & ~3) != bufSize) { - throw new ParcelFormatException( - "File corrupt: history data buffer not aligned " + bufSize); - } else { - if (DEBUG) { - Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize - + " bytes at " + curPos); + mHistoryBufferStartTime = in.readLong(); + mHistoryBuffer.setDataSize(0); + mHistoryBuffer.setDataPosition(0); + + int bufSize = in.readInt(); + int curPos = in.dataPosition(); + if (bufSize >= (mMaxHistoryBufferSize * 100)) { + throw new ParcelFormatException( + "File corrupt: history data buffer too large " + bufSize); + } else if ((bufSize & ~3) != bufSize) { + throw new ParcelFormatException( + "File corrupt: history data buffer not aligned " + bufSize); + } else { + if (DEBUG) { + Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize + + " bytes at " + curPos); + } + mHistoryBuffer.appendFrom(in, curPos, bufSize); + in.setDataPosition(curPos + bufSize); } - mHistoryBuffer.appendFrom(in, curPos, bufSize); - in.setDataPosition(curPos + bufSize); } } + @GuardedBy("this") private void writeHistoryBuffer(Parcel out) { out.writeInt(BatteryStatsHistory.VERSION); out.writeLong(mHistoryBufferStartTime); @@ -2038,6 +2139,7 @@ public class BatteryStatsHistory { out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); } + @GuardedBy("this") private void writeParcelToFileLocked(Parcel p, AtomicFile file) { FileOutputStream fos = null; mWriteLock.lock(); @@ -2066,34 +2168,43 @@ public class BatteryStatsHistory { * Returns the total number of history tags in the tag pool. */ public int getHistoryStringPoolSize() { - return mHistoryTagPool.size(); + synchronized (this) { + return mHistoryTagPool.size(); + } } /** * Returns the total number of bytes occupied by the history tag pool. */ public int getHistoryStringPoolBytes() { - return mNumHistoryTagChars; + synchronized (this) { + return mNumHistoryTagChars; + } } /** * Returns the string held by the requested history tag. */ public String getHistoryTagPoolString(int index) { - ensureHistoryTagArray(); - HistoryTag historyTag = mHistoryTags.get(index); - return historyTag != null ? historyTag.string : null; + synchronized (this) { + ensureHistoryTagArray(); + HistoryTag historyTag = mHistoryTags.get(index); + return historyTag != null ? historyTag.string : null; + } } /** * Returns the UID held by the requested history tag. */ public int getHistoryTagPoolUid(int index) { - ensureHistoryTagArray(); - HistoryTag historyTag = mHistoryTags.get(index); - return historyTag != null ? historyTag.uid : Process.INVALID_UID; + synchronized (this) { + ensureHistoryTagArray(); + HistoryTag historyTag = mHistoryTags.get(index); + return historyTag != null ? historyTag.uid : Process.INVALID_UID; + } } + @GuardedBy("this") private void ensureHistoryTagArray() { if (mHistoryTags != null) { return; diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java index c6683cfc8331..05728eee174f 100644 --- a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java @@ -18,9 +18,13 @@ package com.android.internal.pm.pkg.component; import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.content.Intent; import android.content.IntentFilter; +import android.content.UriRelativeFilter; +import android.content.UriRelativeFilterGroup; +import android.content.pm.Flags; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; @@ -132,6 +136,11 @@ public class ParsedIntentInfoUtils { case "data": result = parseData(intentInfo, res, parser, allowGlobs, input); break; + case "uri-relative-filter-group": + if (Flags.relativeReferenceIntentFilters()) { + result = parseRelRefGroup(intentInfo, pkg, res, parser, allowGlobs, input); + break; + } default: result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input); break; @@ -163,6 +172,197 @@ public class ParsedIntentInfoUtils { } @NonNull + @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) + private static ParseResult<ParsedIntentInfo> parseRelRefGroup(ParsedIntentInfo intentInfo, + ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs, + ParseInput input) throws XmlPullParserException, IOException { + IntentFilter intentFilter = intentInfo.getIntentFilter(); + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUriRelativeFilterGroup); + UriRelativeFilterGroup group; + try { + int action = UriRelativeFilterGroup.ACTION_ALLOW; + if (!sa.getBoolean(R.styleable.AndroidManifestUriRelativeFilterGroup_allow, true)) { + action = UriRelativeFilterGroup.ACTION_BLOCK; + } + group = new UriRelativeFilterGroup(action); + } finally { + sa.recycle(); + } + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + final ParseResult result; + String nodeName = parser.getName(); + switch (nodeName) { + case "data": + result = parseRelRefGroupData(group, res, parser, allowGlobs, input); + break; + default: + result = ParsingUtils.unknownTag("<uri-relative-filter-group>", + pkg, parser, input); + break; + } + + if (result.isError()) { + return input.error(result); + } + } + + if (group.getUriRelativeFilters().size() > 0) { + intentFilter.addUriRelativeFilterGroup(group); + } + return input.success(null); + } + + @NonNull + @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) + private static ParseResult<ParsedIntentInfo> parseRelRefGroupData(UriRelativeFilterGroup group, + Resources res, XmlResourceParser parser, boolean allowGlobs, ParseInput input) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestData); + try { + String str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_path, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, + PatternMatcher.PATTERN_LITERAL, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_pathPrefix, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, + PatternMatcher.PATTERN_PREFIX, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_pathPattern, 0); + if (str != null) { + if (!allowGlobs) { + return input.error( + "pathPattern not allowed here; path must be literal"); + } + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, + PatternMatcher.PATTERN_SIMPLE_GLOB, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_pathAdvancedPattern, 0); + if (str != null) { + if (!allowGlobs) { + return input.error( + "pathAdvancedPattern not allowed here; path must be literal"); + } + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, + PatternMatcher.PATTERN_ADVANCED_GLOB, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_pathSuffix, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH, + PatternMatcher.PATTERN_SUFFIX, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_fragment, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, + PatternMatcher.PATTERN_LITERAL, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_fragmentPrefix, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, + PatternMatcher.PATTERN_PREFIX, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_fragmentPattern, 0); + if (str != null) { + if (!allowGlobs) { + return input.error( + "fragmentPattern not allowed here; fragment must be literal"); + } + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, + PatternMatcher.PATTERN_SIMPLE_GLOB, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_fragmentAdvancedPattern, 0); + if (str != null) { + if (!allowGlobs) { + return input.error( + "fragmentAdvancedPattern not allowed here; fragment must be literal"); + } + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, + PatternMatcher.PATTERN_ADVANCED_GLOB, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_fragmentSuffix, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT, + PatternMatcher.PATTERN_SUFFIX, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_query, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, + PatternMatcher.PATTERN_LITERAL, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_queryPrefix, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, + PatternMatcher.PATTERN_PREFIX, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_queryPattern, 0); + if (str != null) { + if (!allowGlobs) { + return input.error( + "queryPattern not allowed here; query must be literal"); + } + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, + PatternMatcher.PATTERN_SIMPLE_GLOB, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_queryAdvancedPattern, 0); + if (str != null) { + if (!allowGlobs) { + return input.error( + "queryAdvancedPattern not allowed here; query must be literal"); + } + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, + PatternMatcher.PATTERN_ADVANCED_GLOB, str)); + } + + str = sa.getNonConfigurationString( + R.styleable.AndroidManifestData_querySuffix, 0); + if (str != null) { + group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY, + PatternMatcher.PATTERN_SUFFIX, str)); + } + + return input.success(null); + } finally { + sa.recycle(); + } + } + + @NonNull private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo, Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) { IntentFilter intentFilter = intentInfo.getIntentFilter(); diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp index 6e903b3ab56d..1031542eb2e6 100644 --- a/core/jni/android_window_ScreenCapture.cpp +++ b/core/jni/android_window_ScreenCapture.cpp @@ -211,7 +211,7 @@ static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptu } static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject, - jlong screenCaptureListenerObject) { + jlong screenCaptureListenerObject, jboolean sync) { LayerCaptureArgs captureArgs; getCaptureArgs(env, layerCaptureArgsObject, captureArgs); @@ -227,7 +227,7 @@ static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureA sp<gui::IScreenCaptureListener> captureListener = reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject); - return ScreenshotClient::captureLayers(captureArgs, captureListener); + return ScreenshotClient::captureLayers(captureArgs, captureListener, sync); } static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) { @@ -281,7 +281,7 @@ static const JNINativeMethod sScreenCaptureMethods[] = { // clang-format off {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I", (void*)nativeCaptureDisplay }, - {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I", + {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;JZ)I", (void*)nativeCaptureLayers }, {"nativeCreateScreenCaptureListener", "(Ljava/util/function/ObjIntConsumer;)J", (void*)nativeCreateScreenCaptureListener }, diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto index 75e29082508b..1d1f88b01838 100644 --- a/core/proto/android/content/intent.proto +++ b/core/proto/android/content/intent.proto @@ -66,7 +66,7 @@ message IntentProto { optional string identifier = 13 [ (.android.privacy).dest = DEST_EXPLICIT ]; } -// Next Tag: 12 +// Next Tag: 14 message IntentFilterProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -89,6 +89,7 @@ message IntentFilterProto { optional bool get_auto_verify = 10; repeated string mime_groups = 11; optional android.os.PersistableBundleProto extras = 12; + repeated UriRelativeFilterGroupProto uri_relative_filter_groups = 13; } message AuthorityEntryProto { @@ -98,3 +99,23 @@ message AuthorityEntryProto { optional bool wild = 2; optional int32 port = 3; } + +message UriRelativeFilterGroupProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + enum Action { + ACTION_ALLOW = 0; + ACTION_BLOCK = 1; + } + + optional Action action = 1; + repeated UriRelativeFilterProto uri_relative_filters = 2; +} + +message UriRelativeFilterProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + required int32 uri_part = 1; + required int32 pattern_type = 2; + required string filter = 3; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0e0af4db17a0..0171f584a838 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3751,6 +3751,13 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS" android:protectionLevel="internal|role" /> + <!-- Allows an application to manage policy related to content protection. + <p>Protection level: internal|role + @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION" + android:protectionLevel="internal|role" /> + <!-- Allows an application to set device policies outside the current user that are critical for securing data within the current user. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* @@ -3828,6 +3835,18 @@ <permission android:name="android.permission.ACTIVITY_EMBEDDING" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to embed any other apps in untrusted embedding mode without the need + for the embedded app to consent. + <p>For now, this permission is only granted to the Assistant application selected by + the user. + {@see https://developer.android.com/guide/topics/large-screens/activity-embedding#trust_model} + @SystemApi + @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") + @hide + --> + <permission android:name="android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE" + android:protectionLevel="internal|role" /> + <!-- Allows an application to start any activity, regardless of permission protection or exported state. @hide --> @@ -7985,6 +8004,16 @@ <permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" android:protectionLevel="signature|privileged"/> + <!-- @SystemApi + @FlaggedApi("android.content.pm.emergency_install_permission") + Allows each app store in the system image to designate another app in the system image to + update the app store + <p>Protection level: signature|privileged + @hide + --> + <permission android:name="android.permission.EMERGENCY_INSTALL_PACKAGES" + android:protectionLevel="signature|privileged"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 38d2a255f30a..e864872a6815 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Laat die program toe om die seluitsendingmodule te bind om seluitsendingboodskappe aan te stuur wanneer hulle ontvang word. Seluitsendingwaarskuwings word in sommige liggings gelewer om jou oor noodsituasies te waarsku. Kwaadwillige programme kan met die werkverrigting of werking van jou toestel inmeng wanneer \'n noodseluitsending ontvang word."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Bestuur voortgaande oproepe"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Stel \'n program in staat om besonderhede oor voortgaande oproepe op jou toestel te sien, en hierdie oproepe te beheer."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lees seluitsending-boodskappe"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Laat die program toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige programme mag inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lees ingetekende nuus"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 47d2da09b8ae..e91fe9d3ff8e 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"የሕዋስ ስርጭት መልዕክቶች እንደመጡ ለማስተላለፍ መተግበሪያው ከሕዋስ ስርጭት ሞዱሉ ጋር እንዲተሳሰር ያስችለዋል። የሕዋስ ስርጭት ማንቂያዎች አስቸኳይ ሁኔታዎች ሲያጋጥሙ አንዳንድ አካባቢዎች ላይ የሚላኩ ናቸው። የሕዋስ ስርጭት ሲደርስ ተንኮል-አዘል መተግበሪያዎች በመሣሪያዎ አፈጻጸም ወይም አሰራር ላይ ጣልቃ ሊገቡ ይችላሉ።"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"በመካሄድ ላይ ያሉ ጥሪዎችን አስተዳድር"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"አንድ መተግበሪያ በመካሄድ ላይ ስላሉ ጥሪዎች ዝርዝሮችን እንዲመለከት ያስችለዋል።"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"የህዋስ ስርጭት መልዕክቶችን አንብብ"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"መሣሪያህ የህዋስ ስርጭት መልዕክቶች ሲቀበል መተግበሪያው እንዲያነበው ይፈቅድለታል። የህዋስ ስርጭት ማንቂያዎች አስቸኳይ ሁኔታዎች ሲያጋጥሙ አንዳንድ አካባቢዎች ላይ የሚላኩ ናቸው። የህዋስ ስርጭት ሲደርስ ተንኮል አዘል መተግበሪያዎች በመሣሪያህ አፈጻጸም ወይም አሰራር ላይ ጣልቃ ሊገቡ ይችላሉ።"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"የምዝገባ መግቦች አንበብ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 4d6b92f4db5b..ae1f2aab3a6c 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -374,6 +374,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"يسمح للتطبيق بالارتباط بوحدة البث الخلوي لإعادة توجيه رسائل البث الخلوي بينما يتم استقبالها. ويتم تسليم تنبيهات البث الخلوي في بعض المواقع لتحذيرك في حالات الطوارئ. ويمكن أن تؤثر التطبيقات الضارة على أداء الجهاز أو تشغيله عندما يتم تلقي بث خلوي في حالات الطوارئ."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"إدارة المكالمات الجارية"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"يسمح هذا الإذن للتطبيق بمعرفة تفاصيل المكالمات الجارية على جهازك والتحكّم فيها."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"قراءة رسائل بث الخلية"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"السماح للتطبيق بقراءة رسائل بث الخلية التي يتلقاها هذا الجهاز. يتم تسليم اشعارات بث الخلية في بعض المواقع لتحذيرك من حالات طارئة. يمكن أن تتداخل التطبيقات الضارة مع أداء أو تشغيل الجهاز عندما يتم تلقي بث خلية طارئ."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"قراءة الخلاصات المشتركة"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 94d355f4b297..fd8082a7b12f 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"চেল সম্প্ৰচাৰ বাৰ্তাসমূহ লাভ কৰিলে সেইবোৰ ফৰৱাৰ্ড কৰিবলৈ এপ্টোক চেল সম্প্ৰচাৰ মডিউলটোৰ সৈতে সংযুক্ত হ\'বলৈ অনুমতি দিয়ে। আপোনাক জৰুৰীকালীন পৰিস্থিতিসমূহৰ বিষয়ে সতৰ্ক কৰিবলৈ কিছুমান অৱস্থানত চেল সম্প্ৰচাৰ সতৰ্কবাৰ্তাসমূহ ডেলিভাৰ কৰা হয়। কোনো জৰুৰীকালীন চেল সম্প্ৰচাৰ লাভ কৰিলে ক্ষতিকাৰক এপ্সমূহে আপোনাৰ ডিভাইচটোৰ কাৰ্যক্ষমতা অথবা কাৰ্যপ্ৰণালীত হস্তক্ষেপ কৰিব পাৰে।"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"চলি থকা কলসমূহ পৰিচালনা কৰক"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"এটা এপক আপোনাৰ ডিভাইচত চলি থকা কলৰ সবিশেষ চাবলৈ আৰু এই কলসমূহ নিয়ন্ত্ৰণ কৰিবলৈ অনুমতি দিয়ে।"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"চেল সম্প্ৰচাৰৰ বার্তাবোৰ পঢ়ক"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"আপোনাৰ ডিভাইচে লাভ কৰা চেল সম্প্ৰচাৰৰ বার্তাবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। আপোনাক জৰুৰীকালীন পৰিস্থিতিবোৰত সর্তক কৰিবলৈ চেল সম্প্ৰচাৰৰ বার্তাবোৰ প্ৰেৰণ কৰা হয়। জৰুৰীকালীন চেল সম্প্ৰচাৰ লাভ কৰাৰ সময়ত আপোনাৰ ডিভাইচৰ কাৰ্যদক্ষতা বা কাৰ্যপ্ৰণালীত ক্ষতিকাৰক এপবোৰে হস্তক্ষেপ কৰিব পাৰে।"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"আপুনি সদস্যভুক্ত হোৱা ফীডসমূহ পঢ়ক"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 633f6de88a70..a14725be0a84 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Tətbiqə şəbəkə yayım mesajlarını əldə edildiyi anda yönləndirmək üçün şəbəkə yayımı moduluna bağlanmaq icazəsi verir. Şəbəkə yayımı bəzi məkanlarda olan fövqəladə hadisələrlə bağlı Sizi xəbərdar etmək üçün qəbul edilir. Zərərli tətbiqlər fövqəladə şəbəkə yayımı əldə edildiyi zaman cihazın performansına və əməliyyatına müdaxilə edə bilər."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Davam edən zəngləri idarə edin"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Tətbiqə cihazınızda davam edən zənglər haqqında məlumatları görmək və bu zəngləri idarə etmək imkanı verir."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"mobil yayım mesajlarını oxuyur"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Tətbiqə telefonunuz tərəfindən alınmış yayım mesajlarını oxuma icazəsi verir. Telefon yayımı bəzi məkanlarda olan fövqəladə hadisələrlə bağlı sizi xəbərdar etmək üçün qəbul edilir. Zərərli tətbiqlər təcili mobil yayım qəbul edildiyi zaman telefonunun performansına və əməliyyatına müdaxilə edə bilər."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"abunə olunmuş xəbərləri oxuyur"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 83879846e459..91b5b0451bed 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Dozvoljava aplikaciji da se vezuje za modul poruka za mobilne uređaje na lokalitetu da bi prosleđivala poruke za mobilne uređaje na lokalitetu onako kako su primljene. Obaveštenja poruka za mobilne uređaje na lokalitetu se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na performanse ili ometaju rad uređaja kada se primi poruka o hitnom slučaju za mobilne uređaje na lokalitetu."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Upravljanje odlaznim pozivima"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Omogućava aplikaciji da vidi detalje o odlaznim pozivima na uređaju i da kontroliše te pozive."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"čitanje poruka info servisa"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Omogućava aplikaciji da čita poruke info servisa koje uređaj prima. Upozorenja info servisa se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na performanse ili ometaju funkcionisanje uređaja kada se primi poruka info servisa o hitnom slučaju."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"čitanje prijavljenih fidova"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 42c5010397d8..68b89f0d7c31 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Дазваляе праграме звязвацца з модулем сотавай трансляцыі, каб пераадрасоўваць атрыманыя там паведамленні. Абвесткі сотавай трансляцыі дасылаюцца ў некаторыя месцы, каб папярэджваць вас пра надзвычайныя сітуацыі. Шкодныя праграмы могуць негатыўна ўплываць на прадукцыйнасць або працу прылады падчас атрымання паведамленняў сотавай трансляцыі пра надзвычайныя сітуацыі."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Кіраваць уваходнымі выклікамі"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Дазваляе праграме праглядаць на прыладзе падрабязныя звесткі пра ўваходныя выклікі і кіраваць імі."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"чытаць паведамленні базавай станцыі"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Дазваляе прыкладанню чытаць паведамленні базавай станцыі, атрыманыя прыладай. Папярэджанні базавай станцыі дасылаюцца ў некаторыя месцы, каб папярэдзіць вас аб надзвычайных сітуацыях. Шкоднасныя прыкладанні могуць уплываць на прадукцыйнасць ці працу прылады пры атрыманні паведамлення базавай станцыі аб надзвычайнай сітуацыі."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"чытаць падпісаныя каналы"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 181746e107f9..e369102d65aa 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Разрешава на приложението да се обвърже с модула за клетъчно излъчване, за да препраща получените съобщения с клетъчно излъчване. Сигналите с клетъчно излъчване се получават на някои местоположения, за да ви предупредят за спешни случаи. Злонамерените приложения могат да възпрепятстват изпълнението или работата на устройството ви при получаване на такова спешно излъчване."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Управление на текущите обаждания"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Разрешава на приложението да вижда подробности за текущите обаждания на устройството ви и да ги управлява."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"четене на съобщения с клетъчно излъчване"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Разрешава на приложението да чете съобщения с клетъчно излъчване, получени от устройството ви. Сигналите с клетъчно излъчване се получават на някои местоположения, за да ви предупредят за спешни ситуации. Злонамерените приложения могат да възпрепятстват изпълнението или работата на устройството ви при получаване на такова спешно излъчване."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"четене на емисиите с абонамент"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 236fff52473f..ecbc6d5a54db 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"সেল ব্রডকাস্ট মেসেজ পেলে এটি সেল ব্রডকাস্ট মডিউলের সাথে তা যুক্ত করে যাতে সেই মেসেজ ফরওয়ার্ড করা যায়। আপনাকে জরুরি অবস্থা সম্পর্কে সাবধান করতে কিছু লোকেশনে সেল ব্রডকাস্ট অ্যালার্ট মেসেজ ডেলিভার করা হয়। জরুরি সেল ব্রডকাস্ট পাওয়া গেলে ক্ষতিকারক অ্যাপ আপনার ডিভাইসের পারফর্ম্যান্স ও অপারেশনে বাধা সৃষ্টি করতে পারে।"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"অনগোয়িং কল ম্যানেজ করুন"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"আপনার ডিভাইসে অনগোয়িং কলের বিষয়ে বিবরণ দেখতে এবং এই কলগুলি কন্ট্রোল করার জন্য অ্যাপকে অনুমতি দেয়।"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"সেল সম্প্রচার মেসেজ পড়ুন"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"আপনার ডিভাইস দ্বারা প্রাপ্ত সেল সম্প্রচার পড়তে অ্যাপ্লিকেশানটিকে অনুমতি দেয়৷ কয়েকটি স্থানে আপনাকে জরুরি অবস্থার জন্য সতর্ক করতে জরুরি সতর্কতাগুলি বিতরণ করা হয়৷ যখন একটি জরুরি সেল সম্প্রচার প্রাপ্ত হয় তখন ক্ষতিকারক অ্যাপ্লিকেশানগুলি আপনার ডিভাইসের কার্য সম্পাদনা বা কার্যকলাপে প্রতিবন্ধকতার সৃষ্টি করতে পারে৷"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"গ্রাহক হিসেবে নেওয়া ফিডগুলি পড়ে"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 1da26049fa55..5088911a3fb1 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Dopušta aplikaciji da se veže za modul info servisa kako bi prosljeđivala poruke info servisa. Upozorenja koja emitira info servis se isporučuju na nekim lokacijama kako bi vas upozorila na vanredne situacije. Zlonamjerne aplikacije mogu ometati performanse ili rad vašeg uređaja kada primite informacije o vanrednoj situaciji od info servisa."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Upravljanje pozivima u toku"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Omogućava aplikaciji da vidi detalje o pozivima u toku na vašem uređaju i da ih kontrolira."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"čitanje poruka info servisa"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Omogućava aplikaciji čitanje poruka info servisa koje je primio vaš uređaj. Upozorenja koja emitira info servis se isporučuju na nekim lokacijama kako bi vas upozorila na vanredne situacije. Zlonamjerne aplikacije mogu ometati performanse ili rad vašeg uređaja kada primite informaciju o vanrednoj situaciji od info servisa."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"čitanje sadržaja na koje ste pretplaćeni"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 867a34dc7141..1719d531cb70 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permet que l\'aplicació es vinculi al mòdul de difusió mòbil per poder reenviar els missatges de difusió mòbil a mesura que es reben. Les alertes de difusió mòbil s\'entreguen en algunes ubicacions per alertar de situacions d\'emergència. És possible que les aplicacions malicioses interfereixin en el rendiment o en el funcionament del dispositiu quan es rebi una difusió mòbil d\'emergència."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gestiona les trucades en curs"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permet que una aplicació vegi els detalls sobre les trucades en curs al dispositiu i que controli aquestes trucades."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"llegir missatges de difusió mòbil"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permet que l\'aplicació llegeixi missatges de difusió mòbil rebuts pel dispositiu. Les alertes de difusió mòbil s\'entreguen en algunes ubicacions per alertar de situacions d\'emergència. És possible que les aplicacions malicioses interfereixin en el rendiment o en el funcionament del dispositiu quan es rep una difusió mòbil d\'emergència."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"llegir els feeds als quals esteu subscrit"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 2c173731cfa0..d50f4f82aafb 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Umožňuje aplikaci vytvořit vazbu s modulem informačních služeb za účelem přesměrovávání přijatých zpráv informačních služeb. Výstražná upozornění informačních služeb jsou v některých oblastech odesílána za účelem varování před výjimečnými událostmi. Škodlivé aplikace mohou narušit výkon či provoz vašeho zařízení během přijímání zpráv informačních služeb."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Správa probíhajících hovorů"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Umožňuje aplikaci zobrazit podrobnosti o probíhajících hovorech v zařízení a tyto hovory ovládat."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"čtení zpráv informačních služeb"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Umožňuje aplikaci číst zprávy informačních služeb přijaté ve vašem zařízení. Výstražná upozornění informačních služeb jsou v některých oblastech odesílána za účelem varování před výjimečnými událostmi. Škodlivé aplikace mohou narušit výkon či provoz vašeho zařízení během přijímání zpráv informačních služeb."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"čtení zdrojů přihlášených k odběru"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index f04fd757c834..e0fc3ca8a626 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -370,6 +370,10 @@ <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> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <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 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> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index d55ba9f25903..7f0dd1e2391e 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Ermöglicht der App, sich mit dem Cell-Broadcast-Modul zu verbinden, um empfangene Cell-Broadcast-Nachrichten weiterzuleiten. Cell-Broadcast-Benachrichtigungen können in einigen Ländern oder Regionen gesendet werden, um dich bei Notfallsituationen zu warnen. Schädliche Apps können die Leistung oder den Betrieb deines Geräts beeinträchtigen, wenn eine Cell-Broadcast-Notfallbenachrichtigung eingeht."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Aktuelle Anrufe verwalten"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Ermöglicht einer App, Details zu aktuellen Anrufen auf deinem Gerät zu sehen und diese Anrufe zu verwalten."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"Cell Broadcast-Nachrichten lesen"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Ermöglicht der App, von deinem Gerät empfangene Cell Broadcast-Nachrichten zu lesen. Cell Broadcast-Benachrichtigungen werden an einigen Standorten gesendet, um dich über Notfallsituationen zu informieren. Schädliche Apps können die Leistung oder den Betrieb deines Geräts beeinträchtigen, wenn eine Cell Broadcast-Notfallbenachrichtigung eingeht."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"Abonnierte Feeds lesen"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 1346faff143f..366533c3ff02 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Επιτρέπει στην εφαρμογή να συνδέεται στη λειτουργική μονάδα εκπομπής κινητής τηλεφωνίας, προκειμένου να προωθεί τα μηνύματα εκπομπής κινητής τηλεφωνίας κατά τη λήψη τους. Οι ειδοποιήσεις εκπομπής κινητής τηλεφωνίας προβάλλονται σε ορισμένες τοποθεσίες, για να σας προειδοποιήσουν σχετικά με καταστάσεις έκτακτης ανάγκης. Οι κακόβουλες εφαρμογές μπορεί να επηρεάσουν την απόδοση ή τη λειτουργία της συσκευής σας κατά τη λήψη μιας εκπομπής κινητής τηλεφωνίας έκτακτης ανάγκης."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Διαχείριση κλήσεων σε εξέλιξη"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Επιτρέπει σε μια εφαρμογή να βλέπει λεπτομέρειες σχετικά με τις κλήσεις σε εξέλιξη στη συσκευή σας και να τις ελέγχει."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"διαβάζει μηνύματα που έχουν μεταδοθεί μέσω κινητού τηλεφώνου"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων που έχουν μεταδοθεί μέσω κινητού τηλεφώνου και έχουν ληφθεί από τη συσκευή σας. Ειδοποιήσεις που μεταδίδονται μέσω κινητού παραδίδονται σε ορισμένες τοποθεσίες για να σας προειδοποιήσουν για καταστάσεις έκτακτης ανάγκης. Κακόβουλες εφαρμογές ενδέχεται να παρεμποδίσουν την απόδοση ή τη λειτουργία της συσκευής σας κατά τη λήψη μετάδοσης μέσω κινητού σχετικά με μια επείγουσα κατάσταση."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"διαβάζει ροές δεδομένων στις οποίες έχετε εγγραφεί"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 8f6bb190d1d1..4e547102988f 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Manage ongoing calls"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Allows an app to see details about ongoing calls on your device and to control these calls."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"read mobile broadcast messages"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"read subscribed feeds"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 9129b820df6d..4f3b52a42f74 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -370,6 +370,8 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Manage ongoing calls"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Allows an app to see details about ongoing calls on your device and to control these calls."</string> + <string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"Access last known cell identity."</string> + <string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"Allows an app to access to the last known cell identity provided by telephony."</string> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"read cell broadcast messages"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Allows the app to read cell broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"read subscribed feeds"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index dd7f4a72ae7a..240421b2a728 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Manage ongoing calls"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Allows an app to see details about ongoing calls on your device and to control these calls."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"read mobile broadcast messages"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"read subscribed feeds"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index b652512133b4..4b427bc1a84f 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Manage ongoing calls"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Allows an app to see details about ongoing calls on your device and to control these calls."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"read mobile broadcast messages"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"read subscribed feeds"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 5ed9d11cd509..66c24b442e36 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -370,6 +370,8 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Manage ongoing calls"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Allows an app to see details about ongoing calls on your device and to control these calls."</string> + <string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"Access last known cell identity."</string> + <string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"Allows an app to access to the last known cell identity provided by telephony."</string> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"read cell broadcast messages"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Allows the app to read cell broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"read subscribed feeds"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index bc94eed5c145..0d57e4b0220d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite que la app se vincule al módulo de emisión móvil para reenviar los mensajes de este tipo a medida que se reciben. En algunas ubicaciones, se envían alertas de emisión móvil para advertirte en situaciones de emergencia. Es posible que las apps maliciosas interfieran con el rendimiento o el funcionamiento de tu dispositivo cuando recibes una emisión móvil de emergencia."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Administrar llamadas en curso"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite que una app vea detalles sobre las llamadas en curso del dispositivo y las controle."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"Leer mensajes de difusión móvil"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite a la aplicación leer los mensajes de difusión móvil que recibe tu dispositivo. En algunas ubicaciones, las alertas de difusión móvil se envían para informar situaciones de emergencia. Las aplicaciones maliciosas pueden afectar el rendimiento o funcionamiento de tu dispositivo cuando se recibe un un mensaje de difusión móvil de emergencia."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"leer canales suscritos"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index c65e67058b0d..79be741595d3 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite que la aplicación se vincule con el módulo de difusión móvil para reenviar los mensajes de ese tipo en cuanto se reciben. En ciertas ubicaciones se envían alertas de difusión móvil para avisar de situaciones de emergencia. Cuando se recibe una alerta de difusión móvil de emergencia, ciertas aplicaciones malintencionadas podrían interferir en el rendimiento o en el funcionamiento del dispositivo."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gestionar llamadas en curso"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite que una aplicación consulte datos de llamadas que estén en curso en tu dispositivo y controle esas llamadas."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"leer mensajes de difusión móvil"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite que la aplicación lea mensajes de difusión móvil que haya recibido el dispositivo. Las alertas de difusión móvil se envían en algunas ubicaciones para avisar de situaciones de emergencia. Es posible que las aplicaciones malintencionadas interfieran en el rendimiento o en el funcionamiento del dispositivo si se recibe una alerta de difusión móvil de emergencia."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"leer feeds a los que está suscrito el usuario"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 9c7cc1b0b518..a07c4cd5fd3d 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Võimaldab rakendusel luua ühenduse kärjeteadete mooduliga, et saabunud kärjeteateid edasi saata. Kärjeteateid edastatakse mõnes asukohas eriolukorrast teavitamiseks. Pahatahtlikud rakendused võivad seadme toimivust või tööd eriolukorra kärjeteate vastuvõtmisel segada."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Käimasolevate kõnede haldamine"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Võimaldab rakendusel näha teie seadmes käimasolevate kõnede üksikasju ja neid kõnesid hallata."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"mobiilsidesõnumite lugemine"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Võimaldab rakendusel lugeda seadme vastu võetud mobiilsidesõnumeid. Mobiilsidemärguandeid edastatakse mõnes asukohas eriolukorrast teavitamiseks. Pahatahtlikud rakendused võivad segada seadme toimivust või tööd eriolukorra sõnumi vastuvõtmisel."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"loe tellitud kanaleid"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 847fe5621988..b3f2ce203989 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Sare mugikor bidezko iragarpen-modulura lotzeko baimena ematen dio aplikazioari, sare mugikor bidezko iragarpen-mezuak jaso ahala desbideratu ahal izateko. Sare mugikor bidezko iragarpen-alertak kokapen batzuetan entregatzen dira larrialdi-egoeren berri emateko. Sare mugikor bidezko larrialdi-iragarpenak jasotzean, asmo txarreko aplikazioek gailuaren errendimenduari edota funtzionamenduari eragin diezaiokete."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Kudeatu abian dauden deiak"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Gailuak jasotzen dituen deiei buruzko xehetasunak ikusteko eta dei horiek kontrolatzeko baimena ematen die aplikazioei."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"irakurri sare mugikor bidezko igorpen-mezuak"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Gailuak jasotako sare mugikor bidezko igorpen-mezuak irakurtzeko baimena ematen dio aplikazioari. Sare mugikor bidezko igorpen-alertak kokapen batzuetan ematen dira larrialdi-egoeren berri emateko. Asmo txarreko aplikazioek gailuaren errendimendua edo funtzionamendua oztopa dezakete larrialdi-igorpen horietako bat jasotzen denean."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"irakurri harpidetutako jarioak"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 6cdaf78a0e82..7738e1c696bb 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"به برنامه امکان میدهد به مدول پخش سلولی متصل شود تا پیامهای پخش سلولی را به محض دریافت بازارسال کند. هشدارهای پخش سلولی در برخی از موقعیتهای مکانی ارسال میشوند تا موقعیتهای اضطراری را به شما اعلام کنند. وقتی پخش سلولی دریافت میشود، ممکن است برنامههای مخرب در عملکرد یا کارکرد دستگاه شما اختلال ایجاد کنند."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"مدیریت تماسهای درحال انجام"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"به برنامه اجازه میدهد جزئیات تماسهای درحال انجام در دستگاه را ببیند و این تماسها را کنترل کند."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"خواندن پیامهای پخش سلولی"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"به برنامه اجازه میدهد پیامهای پخش سلولی دستگاه شما را بخواند. هشدارهای پخش سلولی در برخی از موقعیتهای مکانی تحویل داده میشوند تا موقعیتهای اضطراری را به شما اعلام کنند. وقتی پخش سلولی دریافت میشود، ممکن است برنامههای مخرب در عملکرد یا کارکرد دستگاه شما اختلال ایجاد کنند."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"خواندن فیدهای مشترک"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index e0dd50d6198a..96e0ad466c70 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Sallii sovelluksen sitoutua solulähetysmoduuliin lähettääkseen solulähetysviestejä edelleen sitä mukaa kun ne saapuvat. Solulähetysilmoitusten avulla ilmoitetaan hätätilanteista joissakin paikoissa. Haitalliset sovellukset voivat häiritä laitteen toimintaa laitteen vastaanottaessa hätätilanteeseen liittyvän solulähetysviestin."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Ylläpidä käynnissä olevia puheluita"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Sovellus voi nähdä tietoja laitteella käynnissä olevista puheluista ja ohjata näitä puheluita."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lue tiedotteita"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Antaa sovelluksen lukea laitteesi vastaanottamia tiedotteita. Tiedotteiden avulla ilmoitetaan hätätilanteista joissakin paikoissa. Haitalliset sovellukset voivat häiritä laitteen toimintaa laitteen vastaanottaessa hätätiedotteen."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lukea tilattuja syötteitä"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 0bb9ee5bcb50..379dce095637 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permet à l\'application d\'établir un lien avec le module de diffusion cellulaire afin de transférer les messages de diffusion cellulaire à mesure de leur réception. Dans certaines régions, des alertes de diffusion cellulaire sont envoyées afin de vous avertir de situations d\'urgence. Des applications malveillantes peuvent interférer avec les performances ou le fonctionnement de votre appareil lors de la réception d\'une alerte d\'urgence par diffusion cellulaire."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gérer les appels en cours"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Autorise une application à afficher les renseignements concernant les appels en cours sur votre appareil et à les gérer."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lire les messages de diffusion cellulaire"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permet à l\'application de lire les messages de diffusion cellulaire que votre appareil reçoit. Dans certaines zones géographiques, des alertes vous sont envoyées afin de vous prévenir en cas de situation d\'urgence. Des applications malveillantes peuvent venir perturber les performances ou le fonctionnement de votre appareil lors de la réception d\'un message de diffusion cellulaire."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lire les flux auxquels vous êtes abonné"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 8956904b7398..455b2da91f4b 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Autorise l\'application à établir une connexion avec le module de diffusion cellulaire afin de transférer les messages reçus via un canal de diffusion cellulaire. Des alertes de diffusion cellulaire sont générées dans certaines régions afin de vous avertir de situations d\'urgence. Des applications malveillantes peuvent interférer avec les performances ou le fonctionnement de votre appareil lors de la réception d\'une alerte d\'urgence par diffusion cellulaire."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gérer les appels en cours"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Autorise une application à afficher les détails concernant les appels en cours sur votre appareil et à contrôler ces appels."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lire les messages reçus via un canal de diffusion cellulaire"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permet à l\'application de lire les messages que votre appareil reçoit via un canal de diffusion cellulaire. Dans certaines zones géographiques, des alertes vous sont envoyées afin de vous prévenir en cas de situation d\'urgence. Les applications malveillantes peuvent venir perturber les performances ou le fonctionnement de votre appareil lorsqu\'un message est reçu via un canal de diffusion cellulaire."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lire les flux auxquels vous êtes abonné"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index b1598b35ba2a..df08dd350b77 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite que a aplicación se vincule ao módulo de difusión móbil para reenviar as mensaxes deste tipo segundo se reciban. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de emerxencia. As aplicacións maliciosas poden interferir no rendemento ou funcionamento do teu dispositivo cando se reciba unha difusión móbil de emerxencia."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Xestionar as chamadas saíntes"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite que unha aplicación controle as chamadas saíntes do teu dispositivo e consulte os detalles sobre elas."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ler mensaxes de difusión móbil"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite á aplicación ler mensaxes de difusión móbil recibidas polo teu dispositivo. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de emerxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión móbil de emerxencia."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ler feeds subscritos"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 023269f23dec..574c510c4ed9 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"સેલ બ્રોડકાસ્ટ સંદેશા પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ચાલી રહેલા કૉલ મેનેજ કરો"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ઍપને તમારા ડિવાઇસ પર ચાલુ કૉલ વિશેની વિગતો જોવાની અને આ કૉલને નિયંત્રિત કરવાની મંજૂરી આપે છે."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"સેલ બ્રોડકાસ્ટ સંદેશા વાંચો"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ઍપ તમારા ડિવાઇસ દ્વારા પ્રાપ્ત થયેલ સેલ બ્રોડકાસ્ટ સંદેશાને વાંચવાની મંજૂરી આપે છે. સેલ બ્રોડકાસ્ટ ચેતવણીઓ તમને ઇમર્જન્સીની સ્થિતિઓ અંગે ચેતવવા માટે કેટલાક સ્થાનોમાં વિતરિત થાય છે. જ્યારે ઇમર્જન્સીનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઓપરેશનમાં હસ્તક્ષેપ કરી શકે છે."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"સબ્સ્ક્રાઇબ કરેલ ફીડ્સ વાંચો"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index a9221d8fdb08..2f08e2a4f831 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"सेल ब्रॉडकास्ट (CBC) मैसेज आते ही उसे दूसरे नंबर पर भेजने के लिए, ऐप्लिकेशन को सेल ब्रॉडकास्ट (CBC) मॉड्यूल पर बाइंड करने की अनुमति देता है. कुछ जगहों में सेल ब्रॉडकास्ट (CBC) अलर्ट आपातकालीन स्थितियों के बारे में चेतावनी देने के लिए भेजा जाता है. नुकसान पहुंचाने वाले ऐप्लिकेशन, आपातकाल में सेल ब्रॉडकास्ट (CBC) मैसेज मिलने पर आपके डिवाइस के काम करते समय या इसके परफ़ॉर्मेंस में रुकावट पैदा कर सकते हैं."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"चल रहे कॉल प्रबंधित करें"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"इससे, ऐप्लिकेशन को आपके डिवाइस पर चल रहे कॉल की जानकारी देखने और उन्हें कंट्रोल करने की अनुमति मिलती है."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल ब्रॉडकास्ट (CBC) मैसेज पढ़ें"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ऐप को, वो सेल ब्रॉडकास्ट (CBC) मैसेज पढ़ने देता है जो आपके डिवाइस को मिले हैं. सेल ब्रॉडकास्ट (CBC) अलर्ट कुछ स्थानों (लोकेशन) पर आपको आपातकालीन स्थितियों की चेतावनी देने के लिए दिए जाते हैं. आपातकालीन सेल ब्रॉडकास्ट (CBC) मिलने पर, धोखा देने वाले ऐप आपके डिवाइस के परफ़ॉर्मेंस या कार्यवाही में दखल दे सकते हैं."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्यता वाली फ़ीड पढ़ें"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 43433a3b6d23..af3bd29dc8ba 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Omogućuje aplikaciji da se poveže s modulom za emitiranje na mobitele kako bi prosljeđivala poruke koje se emitiraju na mobitele po njihovom primitku. Upozorenja značajke emitiranja na mobitele dostavljaju se na nekim lokacijama kako bi upozorila korisnike na hitne situacije. Zlonamjerne aplikacije mogu ometati izvršavanje ili rad vašeg uređaja kada stigne hitno emitiranje na mobitele."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Upravljanje tekućim pozivima"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Omogućuje aplikaciji pregled pojedinosti o tekućim pozivima na uređaju i upravljanje tim pozivima."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"čitaj poruke koje se emitiraju unutar mobilne mreže"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Omogućuje aplikaciji čitanje poruka emitiranih unutar mobilne mreže koje prima vaš uređaj. Upozorenja koja se emitiraju na području mobilne mreže dostavljaju se na nekim lokacijama kako bi upozorila korisnike na hitne situacije. Zlonamjerne aplikacije mogu ometati izvršavanje ili rad vašeg uređaja kada stigne hitno upozorenje koje se emitira unutar mobilne mreže."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"čitanje pretplaćenih feedova"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 64dee26f2d40..882156c7afb0 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Az alkalmazás összekapcsolódhat a cellán belüli üzenetszórás moduljával, hogy az érkezésükkor továbbítani tudja a cellán belüli üzeneteket. Bizonyos helyeken figyelmeztető üzeneteket kaphat a cellán belül a vészhelyzetekről. A rosszindulatú alkalmazások vészhelyzeti cellaüzenet érkezésekor befolyásolhatják az eszköz teljesítményét és működését."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Folyamatban lévő hívások kezelése"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Engedélyezi az alkalmazásnak, hogy az eszközén folyamatban lévő hívások részleteihez hozzáférjen, és vezérelje ezeket a hívásokat."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"cellán belüli üzenetek olvasása"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Lehetővé teszi az alkalmazás számára az eszközre érkező cellán belüli üzenetek olvasását. Bizonyos helyeken figyelmeztető üzeneteket kaphat a cellán belül a vészhelyzetekről. A rosszindulatú alkalmazások befolyásolhatják az eszköz teljesítményét vagy működését vészhelyzeti cellaüzenet érkezésekor."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"feliratkozott hírcsatornák olvasása"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 8a6d802c788f..c85aeb84d6b9 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Թույլ է տալիս հավելվածին կապ հաստատել բջջային հեռարձակման մոդուլի հետ՝ բնակչությանը ծանուցող հաղորդագրությունները վերահասցեավորելու համար։ Որոշ երկրներում այս հաղորդագրություններն օգտագործվում են բնակչությանը արտակարգ իրավիճակների մասին զգուշացնելու համար: Վնասարար հավելվածները կարող են խանգարել ձեր սարքի աշխատանքին, որին ուղարկվում են այս հաղորդագրությունները:"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Ընթացիկ զանգերի կառավարում"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Թույլ է տալիս հավելվածին ձեր սարքում տեսնել ընթացիկ զանգերի մասին տեղեկությունները և կառավարել այդ զանգերը։"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"կարդալ բջջային զեկուցվող հաղորդագրությունները"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Թույլ է տալիս հավելվածին կարդալ ձեր սարքի կողմից ստացված բջջային հեռարձակվող հաղորդագրությունները: Բջջային հեռարձակվող զգուշացումները ուղարկվում են որոշ վայրերում` արտակարգ իրավիճակների մասին ձեզ զգուշացնելու համար: Վնասարար հավելվածները կարող են խանգարել ձեր սարքի արդյունավետությանը կամ շահագործմանը, երբ ստացվում է արտակարգ իրավիճակի մասին բջջային հաղորդում:"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"կարդալ բաժանորդագրված հոսքերը"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 991d69fb1d83..2b76815db47c 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Mengizinkan aplikasi mem-binding ke modul cell broadcast untuk meneruskan pesan cell broadcast saat pesan tersebut diterima. Notifikasi cell broadcast dikirim di beberapa lokasi untuk memperingatkan Anda tentang situasi darurat. Aplikasi berbahaya dapat mengganggu performa atau operasi perangkat saat cell broadcast darurat diterima."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Mengelola panggilan yang sedang berlangsung"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Mengizinkan aplikasi untuk melihat detail panggilan yang sedang berlangsung di perangkat dan mengontrolnya."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"membaca pesan siaran seluler"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Mengizinkan aplikasi membaca pesan siaran seluler yang diterima perangkat Anda. Notifikasi siaran seluler dikirimkan di beberapa lokasi untuk memperingatkan Anda tentang situasi darurat. Aplikasi berbahaya dapat mengganggu kinerja atau operasi perangkat Anda saat siaran seluler darurat diterima."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"baca feed langganan"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 8bab2d721f77..f8b4197d4149 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Heimilar forritinu að bindast endurvarpseiningunni til að framsenda skilaboð frá endurvarpa þegar þau berast. Viðvaranir frá endurvarpa berast á tilteknum stöðum til að vara þig við neyðarástandi. Spilliforrit geta truflað afköst eða virkni tækisins þegar neyðarboð berast frá endurvarpa."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Stjórna símtölum sem eru í gangi"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Leyfir forriti að sjá upplýsingar um og stjórna símtölum sem eru í gangi í tækinu þínu."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lesa skilaboð frá endurvarpa"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Leyfir forriti að lesa skilaboð frá endurvarpa sem tækið móttekur. Viðvaranir frá endurvarpa berast á tilteknum stöðum til að vara þig við neyðarástandi. Spilliforrit geta truflað afköst eða virkni tækisins þegar neyðarboð berast frá endurvarpa."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lesa strauma í áskrift"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index b2180ab3f319..5a674766b3cd 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Consente all\'app di essere associata al modulo di cell broadcast al fine di inoltrare i messaggi di cell broadcast man mano che arrivano. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di situazioni di emergenza. Le app dannose potrebbero interferire con le prestazioni o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gestione delle chiamate in corso"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Consente a un\'app di accedere a dettagli relativi alle chiamate in corso sul tuo dispositivo e di controllare tali chiamate."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lettura di messaggi cell broadcast"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Consente all\'applicazione di leggere i messaggi cell broadcast ricevuti dal dispositivo. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di eventuali situazioni di emergenza. Le app dannose potrebbero interferire con le prestazioni o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lettura feed sottoscritti"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 593668a87e65..270568d58b2a 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"מאפשרת לאפליקציה להתחייב למודול של השידור הסלולרי כדי להעביר הודעות של שידור סלולרי כשהן מתקבלות. התראות שידור סלולרי נשלחות במקומות מסוימים כדי להזהיר אותך מפני מצבי חירום. אפליקציות זדוניות עשויות להפריע לביצועים או לפעולה של המכשיר כאשר מתקבל שידור חירום סלולרי."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ניהול שיחות שנערכות"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"לאפליקציה תהיה אפשרות לראות פרטים על שיחות שנערכות במכשיר ולשלוט בשיחות האלה."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"קריאת הודעות שידור סלולרי"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"מאפשרת לאפליקציה לקרוא הודעות שידור סלולרי שהתקבלו במכשיר שלך. התראות שידור סלולרי נשלחות במקומות מסוימים כדי להזהיר אותך מפני מצבי חירום. אפליקציות זדוניות עשויות להפריע לביצועים או לפעולה של המכשיר שלך כאשר מתקבל שידור חירום סלולרי."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"קריאת עדכוני מינויים"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 3a904b6467bc..98e23247432f 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"受信した緊急速報メールを転送するために、緊急速報メール モジュールにバインドすることをこのアプリに許可します。緊急速報メールは、緊急事態を警告する目的で一部の地域に配信されます。緊急速報メールの受信時に、悪意のあるアプリによってデバイスの動作や処理が妨害される恐れがあります。"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"通話の管理"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"デバイスでの通話に関する詳細の参照と、通話の操作をアプリに許可します。"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"緊急速報メール SMS の読み取り"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"デバイスで受信した緊急速報メール SMS の読み取りをアプリに許可します。緊急速報メールは、緊急事態を警告する目的で一部の地域に配信されます。緊急速報メールの受信時に、悪意のあるアプリによってデバイスの動作や処理が妨害される恐れがあります。"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"登録したフィードの読み取り"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index e77068d8f0d5..f7ce159cb08b 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"საშუალებას აძლევს აპს, დაუკავშირდეს cell broadcast მოდულს იმისთვის, რომ გადაამისამართოს cell broadcast შეტყობინებები მათი მიღებისთანავე. Cell broadcast გაფრთხილებები მიეწოდება ზოგიერთ მდებარეობაზე საგანგებო სიტუაციების შესახებ გაფრთხილების მიზნით. საგანგებო cell broadcast-ის მიღების დროს, მავნე აპებმა შეიძლება ხელი შეუშალონ თქვენი მოწყობილობის ფუნქციონირებას ან ოპერაციებს."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"მიმდინარე ზარების მართვა"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"საშუალებას აძლევს აპს, ნახოს თქვენს მოწყობილობაზე მიმდინარე ზარების დეტალები და აკონტროლოს ეს ზარები."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"მასიური დაგზავნის შეტყობინებების წაკითხვა"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"აპს შეეძლება, წაიკითხოს თქვენს მოწყობილობაზე გამოგზავნილი ქსელის სამაუწყებლო შეტყობინებები. სამაუწყებლო გაფრთხილებები მოგეწოდებათ ზოგიერთ ადგილზე ექსტრემალური სიტუაციების შესახებ გასაფრთხილებლად. ქსელის გადაუდებელი შეტყონიბენის მიღების დროს მავნე აპებმა შეიძლება ხელი შეუშალონ თქვენი მოწყობილობის ფუნქციონირებას ან ოპერაციებს."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"გამოწერილი არხების წაკითხვა"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index e6af91206c67..c459954838fb 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Ұялы таратылым хабарлары алынғаннан кейін, олардың бағытын өзгерту үшін қолданбаға ұялы таратылым модулімен байланыстыруға мүмкіндік береді. Ұялы таратылым ескертулері кей аймақтарда төтенше жағдайлар туралы хабарлау үшін беріледі. Төтенше жағдай туралы ұялы таратылым хабары алынғаннан кейін, зиянды қолданбалар құрылғы жұмысына кедергі келтіруі мүмкін."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Қазіргі қоңырауларды басқару"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Қолданбаға құрылғыдағы қазіргі қоңыраулар туралы мәліметтерді көруге және басқаруға мүмкіндік береді."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ұялы хабар тарату хабарларын оқу"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Қолданбаға ұялы таратылым хабарларын оқу мүмкіндігін береді. Ұялы таратылым дабылдары кейбір аймақтарда төтенше жағдай туралы ескерту үшін қолданылады. Төтенше ұялы хабарлар келгенде залалды қолданбалар құрылғының жұмысына кедергі жасауы мүмкін."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"жазылған ағындарды оқу"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 713352725fde..364db0c50b2b 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"អនុញ្ញាតឱ្យកម្មវិធីភ្ជាប់ទៅម៉ូឌុលការផ្សាយចល័ត ដើម្បីបញ្ជូនសារផ្សាយចល័តបន្ត នៅពេលទទួលបានសារទាំងនោះ។ ការជូនដំណឹងអំពីការផ្សាយចល័តត្រូវបានបញ្ជូនទៅទីតាំងមួយចំនួន ដើម្បីព្រមានអ្នកអំពីស្ថានភាពអាសន្ន។ កម្មវិធីគ្រោះថ្នាក់អាចរំខានដល់ដំណើរការ ឬប្រតិបត្តិការឧបករណ៍របស់អ្នក នៅពេលទទួលបានការផ្សាយចល័តពេលមានអាសន្ន។"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"គ្រប់គ្រងការហៅទូរសព្ទដែលកំពុងដំណើរការ"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"អនុញ្ញាតឱ្យកម្មវិធីមើលព័ត៌មានលម្អិតអំពីការហៅទូរសព្ទដែលកំពុងដំណើរការនៅលើឧបករណ៍របស់អ្នក និងគ្រប់គ្រងការហៅទូរសព្ទទាំងនេះ។"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"អានសារប្រកាសចល័ត"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ឲ្យកម្មវិធីអានសារប្រកាសការហៅដែលឧបករណ៍របស់អ្នកបានទទួល។ ការជូនដំណឹងប្រកាសចល័តត្រូវបានបញ្ជូនទៅទីតាំងមួយចំនួន ដើម្បីព្រមានអ្នកអំពីស្ថានភាពអាសន្ន។ កម្មវិធីព្យាបាទអាចជ្រៀតជ្រែកការអនុវត្ត ឬប្រតិបត្តិការឧបករណ៍របស់អ្នកពេលទទួលការប្រកាសចល័តពេលអាសន្ន។"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"អានអត្ថបទព័ត៌មានបានជាវ"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 1e9414acb8b1..89e2361dea31 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"ಸೆಲ್ ಪ್ರಸಾರವು ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಿದ ರೀತಿಯಲ್ಲಿಯೇ ಫಾರ್ವರ್ಡ್ ಮಾಡಲು, ಸೆಲ್ ಪ್ರಸಾರ ಮಾಡ್ಯುಲ್ ಅನ್ನು ಪ್ರತಿಬಂಧಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಕೆಲವು ಸ್ಥಳಗಳಲ್ಲಿ ತುರ್ತು ಸ್ಥಿತಿಗಳ ಕುರಿತು ನಿಮಗೆ ಎಚ್ಚರಿಸಲು ಸೆಲ್ ಪ್ರಸಾರದ ಎಚ್ಚರಿಕೆಗಳನ್ನು ಕಳುಹಿಸಲಾಗುತ್ತದೆ. ತುರ್ತು ಸೆಲ್ ಪ್ರಸಾರವನ್ನು ಸ್ವೀಕರಿಸಿದಾಗ ನಿಮ್ಮ ಸಾಧನದ ಕಾರ್ಯಾಚರಣೆ ಅಥವಾ ಕಾರ್ಯಕ್ಷಮತೆಗೆ ದುರುದ್ದೇಶಪೂರಿತ ಆ್ಯಪ್ಗಳು ಹಸ್ತಕ್ಷೇಪ ಮಾಡಬಹುದು."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆಗಳ ಕುರಿತಾದ ವಿವರಗಳನ್ನು ನೋಡಲು ಮತ್ತು ಆ ಕರೆಗಳನ್ನು ನಿಯಂತ್ರಿಸಲು ಆ್ಯಪ್ ಒಂದಕ್ಕೆ ಅನುಮತಿಸುತ್ತದೆ."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ಸೆಲ್ ಪ್ರಸಾರದ ಸಂದೇಶಗಳನ್ನು ಓದಿರಿ"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ನಿಮ್ಮ ಸಾಧನದಿಂದ ಸ್ವೀಕರಿಸಿದ ಸೆಲ್ ಪ್ರಸಾರ ಸಂದೇಶಗಳನ್ನು ರೀಡ್ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸೆಲ್ ಪ್ರಸಾರ ಎಚ್ಚರಿಕೆಗಳನ್ನು ತುರ್ತು ಸಂದರ್ಭಗಳಲ್ಲಿ ನಿಮಗೆ ಎಚ್ಚರಿಸುವ ಸಲುವಾಗಿ ಕೆಲವು ಸ್ಥಳಗಳಲ್ಲಿ ವಿತರಿಸಲಾಗುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳು ತುರ್ತು ಸೆಲ್ ಪ್ರಸಾರವನ್ನು ಸ್ವೀಕರಿಸುವಾಗ, ನಿಮ್ಮ ಸಾಧನದ ಕಾರ್ಯಕ್ಷಮತೆ ಇಲ್ಲವೇ ಕಾರ್ಯಾಚರಣೆಯಲ್ಲಿ ಹಸ್ತಕ್ಷೇಪ ಮಾಡಬಹುದು."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ಚಂದಾದಾರ ಫೀಡ್ಗಳನ್ನು ಓದಿ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index f78d0af821ce..a129c9bb0c16 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"셀 브로드캐스트 메시지를 수신하자마자 전달하기 위해 앱이 셀 브로드캐스트 모듈에 연결하도록 허용합니다. 비상 상황임을 알리기 위해 일부 지역에서 셀 브로드캐스트 경고가 전달됩니다. 비상 셀 브로드캐스트를 수신할 때 악성 앱이 기기의 성능이나 작동을 방해할 수 있습니다."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"진행 중인 전화 관리"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"앱에서 기기의 진행 중인 전화에 관한 세부정보를 확인하고 전화를 제어하도록 허용합니다."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"셀 브로드캐스트 메시지 읽기"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"앱이 기기가 수신한 셀 브로드캐스트 메시지를 읽을 수 있도록 합니다. 비상 상황임을 알리기 위해 일부 지역에서 셀 브로드캐스트 경고가 전달됩니다. 비상 셀 브로드캐스트를 수신할 때 악성 앱이 기기의 성능이나 작동을 방해할 수 있습니다."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"가입된 피드 읽기"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index e7ee2e8fc226..b84dcf509207 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Уюк жөнөтүүлөрүнүн билдирүүлөрү келген сайын башка номерге багыттап туруу үчүн колдонмого уюк жөнөтүүлөрүнүн модулу менен байланышууга уруксат берет. Шашылыш уюк жөнөтүүлөрү кээ бир жерлердеги өзгөчө кырдаалдар тууралуу сизге эскертүү үчүн жөнөтүлөт. Зыянкеч колдонмолор шашылыш уюк жөнөтүүлөрү кабыл алынганда түзмөктүн майнаптуулугуна же иштешине жолтоо болушу мүмкүн."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Учурдагы чалууларды башкаруу"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Колдонмого телефонуңузда аткарылып жаткан чалууларды көрүп, аларды көзөмөлдөөгө уруксат берет."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"уюктук берүү билдирүүлөрүн окуу"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Колдонмого түзмөгүңүз кабыл алган уюк берүүнүн билдирүүлөрүн окууга жол берет. Шашылыш эскертүү билдирүүлөрү кээ бир жерлердеги өзгөчө кырдаалдар тууралу сизди эскертүү үчүн жөнөтүлөт. Зыяндуу колдономолор шашылыш эскертүүлөр берилип жатканда, сиздин түзмөктүн иштешине жолтоо болушу мүмкүн."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"жазылган түрмөктөрдү окуу"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 84fea7383ab5..267f60dc8b5f 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"ອະນຸຍາດໃຫ້ແອັບຜູກມັດກັບໂມດູນການກະຈາຍສັນຍານໂທລະສັບເພື່ອສົ່ງຕໍ່ຂໍ້ຄວາມການກະຈາຍສັນຍານໂທລະສັບເມື່ອໄດ້ຮັບມາ. ການເຕືອນການກະຈາຍສັນຍານໂທລະສັບແມ່ນຖືກຈັດສົ່ງໃນບາງສະຖານທີ່ເພື່ອເຕືອນທ່ານໃນກໍລະນີມີເຫດການສຸກເສີນເກີດຂຶ້ນ. ແອັບທີ່ເປັນອັນຕະລາຍອາດລົບກວນປະສິດທິພາບ ຫຼື ການເຮັດວຽກຂອງອຸປະກອນທ່ານເມື່ອໄດ້ຮັບການກະຈາຍສັນຍານໂທລະສັບສຸກເສີນ."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ຈັດການສາຍໂທອອກ"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ອະນຸຍາດໃຫ້ແອັບໃດໜຶ່ງເບິ່ງເຫັນລາຍລະອຽດກ່ຽວກັບສາຍໂທອອກຢູ່ອຸປະກອນຂອງທ່ານ ແລະ ເພື່ອຄວບຄຸມການໂທເຫຼົ່ານີ້."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ອ່ານຂໍ້ຄວາມກະຈາຍສັນຍານຂອງເສົາສັນຍານ"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ອະນຸຍາດໃຫ້ແອັບຯ ສາມາດອ່ານຂໍ້ຄວາມແຈ້ງເຕືອນເຫດສຸກເສີນ ທີ່ໄດ້ຮັບໂດຍອຸປະກອນຂອງທ່ານ. ການແຈ້ງເຕືອນສຸກເສີນທີ່ມີໃຫ້ບໍລິການໃນບາງພື້ນທີ່ ເພື່ອແຈ້ງເຕືອນໃຫ້ທ່ານຮູ້ເຖິງສະຖານະການສຸກເສີນ. ແອັບພລິເຄຊັນທີ່ເປັນອັນຕະລາຍອາດລົບກວນປະສິດທິພາບ ຫຼືການດຳເນີນງານຂອງອຸປະກອນຂອງທ່ານ ເມື່ອໄດ້ການຮັບແຈ້ງເຕືອນສຸກເສີນຈາກສະຖານີມືຖື."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ອ່ານຂໍ້ມູນຟີດທີ່ສະໝັກໄວ້"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 0991daa44e00..4696722d808d 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Programai leidžiama susaistyti transliacijos mobiliuoju modulį, kad būtų galima persiųsti mobiliuoju transliuojamus pranešimus, kai jie gaunami. Transliacijos mobiliuoju įspėjimai pristatomi kai kuriose vietovėse, kad būtų galima įspėti apie kritines situacijas. Kai gaunamas mobiliuoju transliuojamas pranešimas apie kritinę situaciją, kenkėjiškos programos gali trukdyti įrenginiui veikti ar jį naudoti."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Tvarkyti vykstančius skambučius"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Programai leidžiama peržiūrėti išsamią informaciją apie vykstančius skambučius įrenginyje ir valdyti šiuos skambučius."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"skaityti mobiliuoju transliuojamus pranešimus"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Programai leidžiama skaityti mobiliuoju transliuojamus pranešimus, gaunamus jūsų įrenginyje. Mobiliuoju transliuojami įspėjimai pristatomi kai kuriose vietose, kad įspėtų apie kritines situacijas. Kai gaunamas mobiliuoju transliuojamas pranešimas apie kritinę situaciją, kenkėjiškos programos gali trukdyti įrenginiui veikti ar jį naudoti."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"skaityti prenumeruojamus tiekimus"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 68f496a9ad44..79e82cca665d 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Ļauj piesaistīt lietotni šūnu apraides modulim, lai pārsūtītu šūnu apraides ziņojumus, tiklīdz tie tiek saņemti. Šūnu apraides brīdinājumi tiek piegādāti noteiktās atrašanās vietās, lai brīdinātu jūs par ārkārtas situācijām. Ļaunprātīgas lietotnes var traucēt ierīces veiktspēju vai darbības, kad ir saņemts ārkārtas šūnas apraides ziņojums."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Aktīvo zvanu pārvaldība"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Ļauj lietotnei skatīt detalizētu informāciju par aktīvajiem zvaniem jūsu ierīcē, kā arī kontrolēt šos zvanus."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"šūnu apraides ziņojumu lasīšana"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Ļauj lietotnei lasīt ierīcē saņemtos šūnu apraides ziņojumus. Šūnu apraides brīdinājumi tiek piegādāti dažās atrašanās vietās, lai brīdinātu jūs par ārkārtas situācijām. Ļaunprātīgas lietotnes var traucēt ierīces veiktspēju vai darbības, kad ir saņemts ārkārtas šūnas apraides ziņojums."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lasīt abonētās plūsmas"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 8828ab466e14..82cb889da4fc 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Дозволува апликацијата да се врзе со модулот за мобилен пренос за да проследува пораки за мобилен пренос штом ќе се примат. Предупредувањата за мобилно емитување се доставуваат на некои локации за да ве предупредат на итни ситуации. Злонамерните апликации може да пречат во ефикасноста или работењето на вашиот уред кога се прима емитување за итен случај."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Управување со тековни повици"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Дозволува апликацијата да гледа детали за тековните повици на уредот и да ги контролира овие повици."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"прочитај пораки за мобилно емитување"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Овозможува апликациите да ги читаат пораките за мобилно емитување што ги прима вашиот уред. Предупредувањата за мобилно емитување се доставуваат на некои локации, за да ве предупредат на итни ситуации. Злонамерните апликации може да пречат во ефикасноста или работењето на вашиот уред кога се прима емитување за итен случај."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"читај претплатени навестувања на содржина"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index cb0cb33073b5..2bea784b4325 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"സ്വീകരിക്കുന്ന മുറയ്ക്ക് ബ്രോഡ്കാസ്റ്റ് സന്ദേശങ്ങൾ കൈമാറുന്നതിന് സെൽ ബ്രോഡ്കാസ്റ്റ് മോഡ്യൂളിലേക്ക് ബൈൻഡ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു. അടിയന്തര സാഹചര്യങ്ങളെ കുറിച്ച് നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകുന്നതിന് ചില ലൊക്കേഷനുകളിൽ സെൽ ബ്രോഡ്കാസ്റ്റ് അലേർട്ടുകൾ ഡെലിവറി ചെയ്യപ്പെടുന്നു. ഒരു അടിയന്തര സെൽ ബ്രോഡ്കാസ്റ്റ് ലഭിക്കുമ്പോൾ ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ ഉപകരണത്തിന്റെ പ്രകടനത്തെയോ പ്രവർത്തനത്തെയോ തടസപ്പെടുത്താനിടയുണ്ട്."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"നടന്നുകൊണ്ടിരിക്കുന്ന കോളുകൾ മാനേജ് ചെയ്യുക"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"നിങ്ങളുടെ ഉപകരണത്തിൽ നടന്നുകൊണ്ടിരിക്കുന്ന കോളുകളുടെ വിശദാംശങ്ങൾ കാണാനും ഈ കോളുകൾ നിയന്ത്രിക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"സെൽ പ്രക്ഷേപണ സന്ദേശങ്ങൾ റീഡുചെയ്യുക"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"നിങ്ങളുടെ ഉപകരണത്തിൽ ലഭിച്ച സെൽ പ്രക്ഷേപണ സന്ദേശങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. അടിയന്തര സാഹചര്യങ്ങളെക്കുറിച്ച് നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകാനായി ചില ലൊക്കേഷനുകളിൽ നൽകപ്പെടുന്നവയാണ് സെൽ പ്രക്ഷേപണ അലേർട്ടുകൾ. ഒരു അടിയന്തര സെൽ പ്രക്ഷേപണം ലഭിക്കുമ്പോൾ, ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ ഉപകരണത്തിന്റെ പ്രകടനമോ പ്രവർത്തനമോ തടസ്സപ്പെടുത്താനിടയുണ്ട്."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"സബ്സ്ക്രൈബ് ചെയ്ത ഫീഡുകൾ വായിക്കുക"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 9bff0da52df1..5d8f143c9f8f 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Гар утсанд масс мессеж түгээх онцлогийн мессежийг хүлээн авах үед түүнийг шилжүүлэх зорилгоор аппад гар утсанд масс мессеж түгээх модультай холбогдохыг зөвшөөрөх Гар утсанд масс мессеж түгээх онцлогийн сэрэмжлүүлэг нь онцгой нөхцөл байдлын тухай танд анхааруулахын тулд зарим байршилд хүрдэг. Гар утсанд масс мессеж түгээх онцлогийн илгээх онцгой нөхцөл байдлын тухай мессежийг хүлээн авах үед хортой апп таны төхөөрөмжийн гүйцэтгэл эсвэл ажиллагаанд саад учруулж болзошгүй."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Үргэлжилж буй дуудлагыг удирдах"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Аппад таны төхөөрөмж дээрх үргэлжилж буй дуудлагын талаарх дэлгэрэнгүйг харах болон эдгээр дуудлагыг хянахыг зөвшөөрнө."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"үүрэн өргөн дамжууллын мессеж унших"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Апп нь таны төхөөрөмжийн хүлээн авсан үүрэн өргөн дамжуулах мессежийг унших боломжтой. Үүрэн өргөн дамжууллын мэдэгдэл нь яаралтай нөхцөл байдлыг анхааруулах зорилгоор зарим байршлуудад хүрдэг. Хортой апп нь яаралтай үүрэн өргөн дамжууллыг хүлээн авсан үед таны төхөөрөмжийн ажиллагаа болон чадамжид нөлөөлөх боломжтой."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"бүртгүүлсэн хангамжийг унших"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 4e55d1cd7c0d..6654a145fc32 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"सेल प्रसारण मेसेज मिळाल्यानंतर ते फॉरवर्ड करण्यासाठी ॲपला सेल प्रसारण मॉड्यूलमध्ये प्रतिबद्ध करण्याची अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थीतींची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरित केल्या जातात. दुर्भावनापूर्ण अॅप्स आणीबाणी सेल प्रसारण मिळवतात तेव्हा ती तुमच्या डिव्हाइसच्या परफॉर्मन्समध्ये किंवा कामामध्ये कदाचित व्यत्यय आणू शकतात."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"सुरू असलेले कॉल व्यवस्थापित करा"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ॲपला तुमच्या डिव्हाइसवर सुरू असलेल्या कॉलचे तपशील पाहण्याची आणि या कॉलना नियंत्रित करण्याची अनुमती द्या."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारण मेसेज वाचा"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अॅप्स व्यत्यय आणू शकतात."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्यत्व घेतलेली फीड वाचा"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 45e3d9182de5..d51dafecba9d 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Membenarkan apl terikat pada modul siaran sel untuk mengirim semula mesej siaran sel apabila diterima. Makluman siaran sel dihantar di sesetengah lokasi untuk memberi amaran kepada anda tentang situasi kecemasan. Apl hasad boleh mengganggu prestasi atau operasi peranti anda apabila siaran sel kecemasan diterima."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Urus panggilan yang sedang berjalan"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Benarkan apl melihat butiran panggilan yang sedang berjalan pada peranti anda dan mengawal panggilan ini."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"baca mesej siaran sel"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Membolehkan apl membaca mesej siaran sel yang diterima oleh peranti anda. Isyarat siaran sel dihantar di beberapa lokasi untuk memberi amaran kepada anda tentang situasi kecemasan. Apl hasad boleh mengganggu prestasi atau operasi peranti anda apabila siaran sel kecemasan diterima."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"baca suapan langganan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 6aee13e7f79c..a9726a5651fc 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"စာတို ဖြန့်ဝေခြင်းစနစ် မော်ဂျူးကိုပေါင်းရန် အက်ပ်များအား ခွင့်ပြုသည်။ ၎င်းမှာ စာတို ဖြန့်ဝေခြင်းစနစ်သုံး မက်ဆေ့ဂျ်များကို လက်ခံရရှိသည့်အတိုင်း ထပ်ဆင့်ပို့ရန် ဖြစ်သည်။ အချို့တည်နေရာများတွင် သင့်အား အရေးပေါ်အခြေအနေများကို သတိပေးရန် စာတို ဖြန့်ဝေခြင်းစနစ်သုံး သတိပေးချက်များကို ပေးပို့သည်။ အရေးပေါ် စာတို ဖြန့်ဝေခြင်းကို ရရှိသည့်အခါ သံသယဖြစ်နိုင်ဖွယ်ရှိသည့် အက်ပ်များက သင့်စက်၏ စွမ်းဆောင်ရည်နှင့် အော်ပရေးရှင်းတို့ကို အနှောင့်အယှက်ပေးနိုင်သည်။"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"လက်ရှိခေါ်ဆိုမှုများကို စီမံခြင်း"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"သင့်စက်ပစ္စည်းပေါ်ရှိ လက်ရှိခေါ်ဆိုမှုများအကြောင်း အသေးစိတ်များကို ကြည့်ရှုရန်နှင့် ဤခေါ်ဆိုမှုများကို ထိန်းချုပ်ရန် အက်ပ်အား ခွင့်ပြုသည်။"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"စာတိုများ ဖြန့်ဝေခြင်းစနစ်အား ဖတ်ခြင်း"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"အပလီကေးရှင်းကို သင်၏ စက်ပစ္စည်းမှ လက်ခံရရှိသော အများလွှင့်ထုတ်ချက်များကို ဖတ်ရန် ခွင့်ပြုသည်။ အများလွှင့်ထုတ်ချက်များသည် အရေးပေါ်အခြေအနေများကို သင့်အား သတိပေးရန် အချို့ နေရာများတွင် ပို့ပေးသည်။ အရေးပေါ်သတိပေးချက် ထုတ်လွှင့်ချက်ကို လက်ခံရရှိချိန်တွင်အန္တရာယ် ဖြစ်စေနိုင်သော အပလီကေးရှင်းများသည် သင့်စက်ပစ္စည်း၏ လုပ်ငန်းလည်ပတ်မှုနှင့် စွမ်းဆောင်မှုကို ဝင်စွက်ဖက်နိုင်သည်။"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"အမည်သွင်းထားသောဖိဖ့်များကို ဖတ်ခြင်း"</string> @@ -802,7 +806,7 @@ <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"အစမြင်ကွင်း ခွင့်ပြုချက် အသုံးပြုမှု"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"အက်ပ်တစ်ခုအတွက် ခွင့်ပြုချက်စတင်အသုံးပြုမှုကို ကိုင်ဆောင်သူအား ခွင့်ပြုသည်။ ပုံမှန်အက်ပ်များအတွက် ဘယ်သောအခါမျှ မလိုအပ်ပါ။"</string> <string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"ခွင့်ပြုသည့် ဆုံးဖြတ်ချက်များကို စတင်ကြည့်ခြင်း"</string> - <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ခွင့်ပြုထားသည့်အက်ပ်အား ခွင့်ပြုသည့်ဆုံးဖြတ်ချက်များကို ကြည့်နိုင်ရန်အတွက် စခရင်စတင်ရန် ခွင့်ပြုနိုင်သည်။ သာမန်အက်ပ်များအတွက် မည်သည့်အခါမျှ မလိုအပ်နိုင်ပါ။"</string> + <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"ခွင့်ပြုထားသည့်အက်ပ်အား ခွင့်ပြုသည့်ဆုံးဖြတ်ချက်များကို ကြည့်နိုင်ရန်အတွက် စခရင်စတင်ရန် ခွင့်ပြုနိုင်သည်။ သာမန်အက်ပ်များအတွက် မည်သည့်အခါမှ မလိုအပ်နိုင်ပါ။"</string> <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"အက်ပ်ဝန်ဆောင်မှုများကို စတင်ကြည့်ခြင်း"</string> <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"ဝန်ဆောင်မှုအချက်အလက်ကိုများကို ခွင့်ပြုချက်ရထားသည့် အက်ပ်အား စတင်ကြည့်နိုင်ရန် ခွင့်ပြုသည်။"</string> <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"နမူနာနှုန်းမြင့်သော အာရုံခံစနစ်ဒေတာကို သုံးပါ"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 4965d90bc18a..822218d16bfa 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Tillat at denne appen binder seg til modulen for kringkastede meldinger for å videresende kringkastede meldinger når de mottas. Kringkastede varsler leveres noen steder for å advare deg om nødssituasjoner. Skadelige apper kan forstyrre ytelsen eller funksjonen til enheten din når en kringkastet nødmelding mottas."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Administrer pågående anrop"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Gir en app tillatelse til å se informasjon om pågående anrop på enheten og til å kontrollere disse anropene."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lese kringkastede meldinger"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Tillater at appen kan lese kringkastede meldinger enheten din mottar. Kringkastede varsler leveres noen steder for å advare deg om nødssituasjoner. Skadelige apper kan forstyrre ytelsen eller funksjonen til enheten din når en kringkastet nødmelding mottas."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lese abonnement på nyhetskilder"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 739227647157..de85e5caaebd 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी म्यासेजहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"जारी रहेका कलहरू व्यवस्थापन गर्न"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"तपाईं यो एपलाई अनुमति दिनुभयो यस एपले तपाईंको डिभाइसमा जारी रहेका कलसम्बन्धी विवरण हेर्न र ती कलहरू नियन्त्रण गर्न सक्छ।"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारित म्यासेजहरू पढ्नुहोस्"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण म्यासेजहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपत्कालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपत्कालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्य बनाइका फिडहरू पढ्नुहोस्"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index dd6f3a69e46c..72a7e68577d2 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Hiermee kan de app de module voor cell broadcasts binden om cell broadcast-berichten door te sturen als die worden ontvangen. Cell broadcast-waarschuwingen worden op bepaalde locaties verzonden om je te waarschuwen voor noodsituaties. Schadelijke apps kunnen de prestaties of verwerking van je apparaat verstoren als een bericht met een noodmelding wordt ontvangen."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Actieve gesprekken beheren"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Hiermee kan een app informatie over actieve gesprekken op je apparaat bekijken en deze gesprekken beheren."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"infodienstberichten lezen"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Toestaan dat de app infodienstberichten leest die worden ontvangen op je apparaat. Infodienstberichten worden verzonden naar bepaalde locaties om u te waarschjeen voor noodsituaties. Schadelijke apps kunnen de prestaties of verwerking van je apparaat verstoren wanneer een infodienstbericht wordt ontvangen."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"Geabonneerde feeds lezen"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index bd4653b0e775..1575fd8b8823 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"ସେଲ୍ ପ୍ରସାରଣ ମେସେଜ୍ ପ୍ରାପ୍ତ ହେବା ପରେ ସେଗୁଡ଼ିକୁ ଫର୍ୱାର୍ଡ କରିବା ପାଇଁ ଆପ୍କୁ ସେଲ୍ ପ୍ରସାରଣ ମଡ୍ୟୁଲ୍ ସହିତ ସଂଯୁକ୍ତ କରିବାକୁ ଅନୁମତି ଦିଏ। ଜରୁରୀକାଳୀନ ପରିସ୍ଥିତିରେ ଆପଣଙ୍କୁ ଚେତାବନୀ ଦେବା ପାଇଁ କିଛି ଲୋକେସନ୍ରେ ସେଲ୍ ପ୍ରସାରଣ ଆଲର୍ଟ ବିତରଣ କରାଯାଇଥାଏ। ଏକ ଜରୁରୀକାଳୀନ ସେଲ୍ ପ୍ରସାରଣ ପ୍ରାପ୍ତ ହେବା ସମୟରେ କିଛି କ୍ଷତିକାରକ ଆପ୍ସ ହୁଏତ ଆପଣଙ୍କର ଡିଭାଇସ୍ର କାର୍ଯ୍ୟଦକ୍ଷତା କିମ୍ବା କାର୍ଯ୍ୟ ପ୍ରକ୍ରିୟାରେ ହସ୍ତକ୍ଷେପ କରିପାରେ।"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ଚାଲୁଥିବା କଲଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ଏକ ଆପକୁ ଆପଣଙ୍କ ଡିଭାଇସରେ ଚାଲୁଥିବା କଲଗୁଡ଼ିକର ବିବରଣୀ ଦେଖିବା ଓ ଏହି କଲଗୁଡ଼ିକୁ ନିୟନ୍ତ୍ରଣ କରିବା ପାଇଁ ଅନୁମତି ଦେଇଥାଏ।"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ସେଲ୍ ବ୍ରଡ୍କାଷ୍ଟ ମେସେଜ୍ ପଢ଼ନ୍ତୁ"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ଆପଣଙ୍କ ଡିଭାଇସ୍ରେ ପ୍ରାପ୍ତ ହୋଇଥିବା ସେଲ୍ ବ୍ରଡ୍କାଷ୍ଟ ମେସେଜ୍ ପଢିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। ଜରୁରୀକାଳୀନ ଅବସ୍ଥା ବିଷୟରେ ଆପଣଙ୍କୁ ସତର୍କ କରାଇବାକୁ କିଛି ଲୋକେଶନ୍ରେ ସେଲ୍ ବ୍ରଡ୍କାଷ୍ଟ ସତର୍କ ଡେଲିଭର୍ କରାଯାଇଥାଏ। ଏକ ଜରୁରୀକାଳୀନ ସେଲ୍ ବ୍ରଡ୍କାଷ୍ଟ ପ୍ରାପ୍ତ ହେବାପରେ ହାନୀକାରକ ଆପ୍ ଆପଣଙ୍କ ଡିଭାଇସ୍ର କାର୍ଯ୍ୟକ୍ଷମତା କିମ୍ବା ସଞ୍ଚାଳନାରେ ବାଧା ପହଞ୍ଚାଇପାରନ୍ତି।"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ସବସ୍କ୍ରାଇବ୍ ହୋଇଥିବା ଫୀଡ୍କୁ ପଢ଼ନ୍ତୁ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index f16234407dc0..01f4fe143694 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"ਐਪ ਨੂੰ ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਸੁਨੇਹਿਆਂ ਦੇ ਪ੍ਰਾਪਤ ਹੁੰਦੇ ਹੀ ਉਨ੍ਹਾਂ ਨੂੰ ਅੱਗੇ ਭੇਜਣ ਲਈ ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਮਾਡਿਊਲ ਨਾਲ ਜੋੜਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ। ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਅਲਰਟ ਤੁਹਾਨੂੰ ਐਮਰਜੈਂਸੀ ਸਥਿਤੀਆਂ ਦੀ ਚਿਤਾਵਨੀ ਦੇਣ ਲਈ ਕੁਝ ਟਿਕਾਣਿਆਂ \'ਤੇ ਪ੍ਰਦਾਨ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਭੈੜੀਆਂ ਐਪਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਾਰਗੁਜ਼ਾਰੀ ਜਾਂ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾ ਸਕਦੀਆਂ ਹਨ ਜਦੋਂ ਇੱਕ ਐਮਰਜੈਂਸੀ ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ਜਾਰੀ ਕਾਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ਐਪ ਨੂੰ ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਜਾਰੀ ਕਾਲਾਂ ਬਾਰੇ ਵੇਰਵੇ ਦੇਖਣ ਅਤੇ ਇਹਨਾਂ ਕਾਲਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰਨ ਦਿਓ।"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ਸੈਲ ਪ੍ਰਸਾਰਨ ਸੁਨੇਹੇ ਪੜ੍ਹੋ"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਵੱਲੋਂ ਪ੍ਰਾਪਤ ਕੀਤੇ ਸੈੱਲ ਪ੍ਰਸਾਰਣ ਸੁਨੇਹੇ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਸੈੱਲ ਪ੍ਰਸਾਰਣ ਚਿਤਾਵਨੀਆਂ ਤੁਹਾਨੂੰ ਸੰਕਟਕਾਲੀਨ ਸਥਿਤੀਆਂ ਦੀ ਚਿਤਾਵਨੀ ਦੇਣ ਲਈ ਕੁਝ ਨਿਰਧਾਰਤ ਟਿਕਾਣਿਆਂ ਤੇ ਪ੍ਰਦਾਨ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਖਰਾਬ ਐਪਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਪ੍ਰਦਰਸ਼ਨ ਜਾਂ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾ ਸਕਦੀਆਂ ਹਨ ਜਦੋਂ ਇੱਕ ਸੰਕਟਕਾਲੀਨ ਸੈੱਲ ਪ੍ਰਸਾਰਣ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ਸਬਸਕ੍ਰਾਈਬ ਕੀਤੇ ਫੀਡਸ ਪੜ੍ਹੋ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 7097503f81ea..375053edee04 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Zezwala aplikacji powiązać się z modułem komunikatów z sieci komórkowej, aby przekazywać je w momencie, w którym są otrzymywane. W niektórych lokalizacjach komunikaty alarmowe z sieci komórkowej są dostarczane, aby ostrzec Cię o sytuacjach zagrożenia. Złośliwe aplikacje mogą wpływać na działanie urządzenia lub zakłócać je po nadejściu komunikatu alarmowego z sieci komórkowej."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Zarządzaj połączeniami w toku"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Zezwala aplikacji zobaczyć detale dotyczące połączeń w toku na Twoim urządzeniu i na kontrolę tych połączeń."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"odczyt komunikatów z sieci komórkowej"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Zezwala aplikacji na odczyt komunikatów z sieci komórkowej odebranych na urządzeniu. Komunikaty alarmowe z sieci komórkowej są dostarczane w niektórych lokalizacjach w celu ostrzeżenia Cię o sytuacjach zagrożenia. Złośliwe aplikacje mogą wpływać na wydajność lub zakłócać działanie urządzenia po odebraniu komunikatu alarmowego z sieci komórkowej."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"czytanie subskrybowanych źródeł"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 49b4be9eaedc..9bec567a3fc3 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite que o app se vincule ao módulo de transmissão celular para encaminhar mensagens de transmissão celular assim que elas forem recebidas. Alertas de transmissão celular são recebidos em alguns locais para avisar sobre situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento do dispositivo quando uma transmissão celular de emergência é recebida."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gerenciar chamadas em andamento"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite que um app veja detalhes sobre chamadas em andamento no seu dispositivo e as controle."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ler mensagens de difusão celular"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ler feeds inscritos"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index f5fd19f95916..b1be29f792e3 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite que a app se vincule ao módulo de difusão celular para encaminhar mensagens de difusão celular à medida que são recebidas. Os alertas de difusão celular são fornecidos em algumas localizações para avisar sobre situações de emergência. As aplicações maliciosas podem interferir com o desempenho ou funcionamento do seu dispositivo quando for recebida uma difusão celular de emergência."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gerir chamadas em curso"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite que uma app veja detalhes acerca das chamadas em curso no seu dispositivo e controle essas chamadas."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ler mensagens de transmissão celular"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite que a app leia mensagens de transmissão celular recebidas pelo seu dispositivo. Os alertas de transmissão celular são fornecidos em algumas localizações para avisá-lo sobre situações de emergência. As aplicações maliciosas podem interferir com o desempenho ou funcionamento do seu dispositivo quando for recebida uma transmissão celular de emergência."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ler feeds subscritos"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 49b4be9eaedc..9bec567a3fc3 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite que o app se vincule ao módulo de transmissão celular para encaminhar mensagens de transmissão celular assim que elas forem recebidas. Alertas de transmissão celular são recebidos em alguns locais para avisar sobre situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento do dispositivo quando uma transmissão celular de emergência é recebida."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Gerenciar chamadas em andamento"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite que um app veja detalhes sobre chamadas em andamento no seu dispositivo e as controle."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ler mensagens de difusão celular"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"ler feeds inscritos"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index e34f9895dad8..b6029f77bb73 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite aplicației să se conecteze la modulul de transmisie celulară pentru a redirecționa mesajele cu transmisie celulară pe măsură ce le primește. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a te avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului când e primită o transmisie celulară de urgență."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Să gestioneze apelurile în desfășurare"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite unei aplicații să vadă detalii despre apelurile în desfășurare de pe dispozitiv și să gestioneze apelurile respective."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"citește mesajele cu transmisie celulară"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitiv. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a te avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului când e primită o transmisie celulară de urgență."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"citire feeduri abonat"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index d37517987545..7d3b869f4f5e 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Приложение сможет выполнить привязку к модулю оповещения населения, чтобы пересылать сообщения широковещательных SMS-служб сразу после их получения. В некоторых странах эти сообщения используются для информирования об экстренных ситуациях. Вредоносное ПО может помешать работе устройства, на которое поступают такие сообщения."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Управление текущими звонками"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Приложение сможет управлять текущими звонками на вашем устройстве, а также получит доступ к сведениям о них"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"Читать сообщения массовой рассылки"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Приложение получит доступ к сообщениям широковещательных SMS-служб, которые в некоторых странах используются для информирования населения об экстренных ситуациях. Вредоносные программы могут помешать работе устройства, на которое поступают такие сообщения."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"Просмотр фидов пользователя"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index c62fe33293ae..12cb28aea668 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"සෙල් විකාශන පණිවිඩ ලැබුණු විට ඒවා යොමු කිරීම සඳහා සෙල් විකාශන මොඩියුලයට බැඳීමට යෙදුමට ඉඩ දෙයි. හදිසි අවස්ථා පිළිබඳව ඔබට අනතුරු ඇඟවීම සඳහා සෙල් විකාශන ඇඟවීම් සමහර ස්ථානවල ලබා දෙනු ලැබේ. හදිසි සෙල් විකාශනයක් ලැබෙන අවස්ථාවකදී, අනිෂ්ට යෙදුම්වලින් ඔබගේ උපාංග කාර්ය සාධනයට හෝ මෙහෙයුමට බාධා සිදු විය හැකිය."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"සිදු වෙමින් පවතින ඇමතුම් කළමනාකරණය කරන්න"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ඔබගේ උපාංගයේ සිදු වෙමින් පවතින ඇමතුම් පිළිබඳ විස්තර බැලීමට සහ මෙම ඇමතුම් පාලනය කිරීමට යෙදුමකට ඉඩ දෙයි."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"සෙල් ප්රචාරණ පණිවිඩ කියවීම"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ඔබගේ උපාංගයට ලැබුණු සෙල් විකාශන පණිවිඩ කියවීමට යෙදුමට අවසර දෙන්න. ඔබට හදිසි අවස්ථාවන් පිළිබඳ අනතුරු ඇඟවීමට සෙල් විකාශන පණිවිඩ ඇතැම් ස්ථානවල සිට යවනු ලබයි. හදිසි සෙල් විකාශන ලැබෙන අවස්ථාවකදී, අනිෂ්ට යෙදුම් මඟින් ඔබගේ උපාංගයට කාර්ය සාධනයට හෝ ක්රියකරණයට බාධා සිදුවිය හැක."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"දායක වූ සංග්රහ කියවීම"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 61ad94de3b3a..e2161aba7ce1 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Umožňuje aplikácii spojiť sa s modulom správ informačných služieb s cieľom preposielať prichádzajúce správy informačných služieb. Správy informačných služieb sa doručujú na určitých miestach a upozorňujú na tiesňové situácie. Škodlivé aplikácie môžu pri prijatí správy informačnej služby narušiť výkonnosť alebo prevádzku vášho zariadenia."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Správa prebiehajúcich hovorov"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Povolí aplikácii čítať podrobnosti o prebiehajúcich hovoroch v zariadení a ovládať ich."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"čítať správy informačných služieb"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Umožňuje aplikácii čítať správy informačných služieb prijaté vaším zariadením. Správy informačných služieb sa doručujú na určitých miestach a upozorňujú na tiesňové situácie. Škodlivé aplikácie môžu pri prijatí správy informačnej služby narušiť výkonnosť alebo prevádzku vášho zariadenia."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"čítať odoberané informačné kanály"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 79e0fa3e83cd..54cc786a8877 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Aplikaciji omogoča povezovanje z modulom za oddaje v celici, da posreduje sporočila oddaj v celici, takoj ko jih prejme. Na nekaterih lokacijah so opozorila oddaj v celici dostavljena, da vas opozorijo na izredne razmere. Zlonamerne aplikacije lahko vplivajo na delovanje naprave, ko prejme sporočilo oddaje v celici."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Upravljanje aktivnih klicev"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Aplikaciji dovoljuje ogled podrobnosti o aktivnih klicih v napravi in upravljanje s temi klici."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"branje sporočil oddaje v celici"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Omogoča aplikaciji branje sporočil oddaje v celici, ki jih prejme naprava. Opozorila oddaje v celici so dostavljena na nekaterih lokacijah, da vas opozorijo na izredne razmere. Zlonamerne aplikacije lahko vplivajo na delovanje naprave, ko dobi sporočilo oddaje v celici."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"branje naročenih virov"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index a16059149d20..3bd5158c5b83 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Lejon që aplikacioni të lidhet me modulin e transmetimit celular për t\'i transferuar mesazhet e transmetimit celular menjëherë kur merren. Sinjalizimet e transmetimit celular dërgohen në disa vendndodhje për të të paralajmëruar për situata urgjente. Aplikacionet keqdashëse mund të ndërhyjnë në cilësinë e funksionimit ose në veprimin e pajisjes sate kur merret një transmetim celular urgjent."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Menaxho telefonatat në vazhdim"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Lejon që një aplikacion të shikojë detaje rreth telefonatave në vazhdim dhe t\'i kontrollojë këto telefonata."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"lexo mesazhet e transmetimit të qelizës"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Lejon aplikacionin të lexojë mesazhet e transmetimit të qelizës, të marra nga pajisja jote. Alarmet e transmetimit të qelizës dërgohen në disa vendndodhje për të të paralajmëruar në situata urgjente. Aplikacionet keqdashëse mund të ndërhyjnë në veprimtarinë ose operacionin e pajisjes tënde kur merret një transmetim urgjent i qelizës."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"lexo informacione të abonuara"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index e1f06de4dcfa..0ea7b6e03ec1 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -371,6 +371,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Дозвољава апликацији да се везује за модул порука за мобилне уређаје на локалитету да би прослеђивала поруке за мобилне уређаје на локалитету онако како су примљене. Обавештења порука за мобилне уређаје на локалитету се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на перформансе или ометају рад уређаја када се прими порука о хитном случају за мобилне уређаје на локалитету."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Управљање одлазним позивима"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Омогућава апликацији да види детаље о одлазним позивима на уређају и да контролише те позиве."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"читање порука инфо сервиса"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на перформансе или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"читање пријављених фидова"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index dc9c19487266..12e24b697bb5 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Tillåter att appen binds till cellsändningsmodulen så att massutskick via sms kan vidarebefordras vid mottagandet. I vissa områden används massutskick via sms för att varna om nödsituationer. Skadliga appar kan påverka enhetens prestanda eller funktioner när ett massutskick via sms tas emot."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Hantera pågående samtal"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Tillåter att en app får åtkomst till information om pågående samtal på enheten och kan kontrollera dessa samtal."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"läsa SMS-meddelanden"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Appen tillåts läsa SMS som skickas till din enhet. På vissa platser skickas SMS för att varna för nödsituationer. Skadliga appar kan påverka enhetens prestanda eller funktionalitet när du får ett meddelande om en nödsituation via SMS."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"läsa flöden som du prenumererar på"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index c5979ddb410b..4a999459622f 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Huruhusu programu ipachikwe katika sehemu ya matangazo ya simu ili isambaze ujumbe wa matangazo ya simu unapopokewa. Arifa za matangazo ya simu huwasilishwa katika maeneo mengine ili kukuonya juu ya hali za dharura. Huenda programu hasidi zikatatiza utendaji au shughuli ya kifaa chako matangazo ya simu ya dharura yanapopokewa."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Dhibiti simu zinazoendelea"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Huruhusu programu kuangalia maelezo kuhusu simu zinazoendelea kwenye kifaa chako na kuzidhibiti."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"soma mawasiliano ya matangazo ya simu"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Huruhusu programu kusoma mawasiliano ya matangazo ya simu yaliyoingia kwenye kifaa chako. Arifa za matangazo ya simu huwasilishwa katika maeneo mengine ili kukuonya juu ya hali za dharura. Huenda programu hasidi zikatatiza utendajikazi au shughuli ya kifaa chako wakati matangazo ya simu ya dharura yameingia."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"kusoma mipasho kutoka vyanzo unavyofuatilia"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 5017e145c7cf..20f12c43ca6e 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"செல் பிராட்காஸ்ட் மெசேஜ்களைப் பெறும்போதெல்லாம் அவற்றை முன்னனுப்பும் பொருட்டு, ஆப்ஸை செல் பிராட்காஸ்ட் மாடியூலோடு இணைக்கும். சில இடங்களில் அவசர சூழ்நிலைகளின் போது உங்களை எச்சரிக்க செல் பிராட்காஸ்ட் விழிப்பூட்டல்கள் அனுப்பப்படும். அவசரநிலை செல் பிராட்காஸ்ட்டைப் பெறும்போது, தீங்கிழைக்கும் ஆப்ஸ் உங்கள் சாதனத்தின் செயல்திறனுக்கோ செயல்பாட்டிற்கோ இடையூறு விளைவிக்கக்கூடும்."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"செயலில் உள்ள அழைப்புகளை நிர்வகித்தல்"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"உங்கள் சாதனத்தில், செயலில் உள்ள அழைப்புகள் குறித்த விவரங்களைப் பார்க்கவும் அந்த அழைப்புகளை நிர்வகிக்கவும் ஆப்ஸை அனுமதிக்கும்."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"செல் அலைபரப்புச் செய்திகளைப் படித்தல்"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"உங்கள் சாதனத்தில் பெறப்படும் செல் அலைபரப்புச் செய்திகளைப் படிப்பதற்குப் ஆப்ஸை அனுமதிக்கிறது. அவசரநிலை சூழ்நிலைகளை உங்களுக்கு எச்சரிக்கைச் செய்வதற்கு சில இடங்களில் செல் அலைபரப்பு விழிப்பூட்டல்கள் வழங்கப்படும். அவசரநிலை மொபைல் அலைபரப்புப் பெறப்படும்போது உங்கள் சாதனத்தின் செயல்திறன் அல்லது செயல்பாட்டுடன் தீங்கிழைக்கும் ஆப்ஸ் அதைத் தடுக்கலாம்."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"குழுசேர்ந்த ஊட்டங்களைப் படித்தல்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 624b2c917f0a..ec58281643b6 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార మెసేజ్లను స్వీకరించినప్పుడు, వాటిని ఫార్వర్డ్ చేయడానికి సెల్ ప్రసార మాడ్యూల్కు కట్టుబడి ఉండేందుకు యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"కొనసాగుతున్న కాల్స్ను మేనేజ్ చేయండి"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"మీ పరికరంలో కొనసాగుతున్న కాల్స్ను చూడటానికి అలాగే వాటిని కంట్రోల్ చేయడానికి ఒక యాప్కు అనుమతిస్తోంది."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార మెసేజ్లను చదవడం"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"చందా చేయబడిన ఫీడ్లను చదవడం"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 155598445390..0da6af7e64a9 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"อนุญาตให้แอปเชื่อมโยงกับโมดูลการส่งข้อมูลเตือนภัยทางมือถือ (CB) เพื่อส่งต่อข้อความจากการส่งข้อมูลเตือนภัยทางมือถือ (CB) ทันทีที่ได้รับ ระบบจะส่งการแจ้งเตือนจากการส่งข้อมูลเตือนภัยทางมือถือ (CB) ในบางตำแหน่งเพื่อแจ้งเตือนคุณเกี่ยวกับสถานการณ์ฉุกเฉิน แอปที่เป็นอันตรายอาจรบกวนประสิทธิภาพหรือการทำงานของอุปกรณ์เมื่อได้รับการส่งข้อมูลเตือนภัยทางมือถือ (CB) ในกรณีฉุกเฉิน"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"จัดการสายที่สนทนา"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"อนุญาตแอปเพื่อดูรายละเอียดเกี่ยวกับสายที่สนทนาอยู่บนโทรศัพท์และเพื่อควบคุมสายสนทนาเหล่านี้"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"อ่านข้อความที่ได้รับจากสถานีมือถือ"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"อนุญาตให้แอปอ่านข้อความจากสถานีมือถือที่อุปกรณ์ได้รับ การแจ้งเตือนทางมือถือมีให้บริการในบางพื้นที่ โดยจะแจ้งเตือนคุณเกี่ยวกับสถานการณ์ฉุกเฉิน แอปที่เป็นอันตรายอาจเข้าแทรกแซงการทำงานของอุปกรณ์เมื่อได้รับข้อความแจ้งเตือนฉุกเฉิน"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"อ่านฟีดข้อมูลที่สมัครไว้"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 25422a59acba..cf3831294832 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Nagbibigay-daan sa app na mag-bind sa module ng cell broadcast para makapagpasa ng mga mensahe ng cell broadcast pagkatanggap sa mga ito. Inihahatid ang mga alerto ng cell broadcast sa ilang lokasyon para balaan ka tungkol sa mga emergency na sitwasyon. Posibleng makasagabal ang mga nakakahamak na app sa performance o pagpapatakbo ng iyong device kapag nakatanggap ito ng emergency na cell broadcast."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Pamahalaan ang mga kasalukuyang tawag"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Pinapayagan ang app na makita ang mga detalye tungkol sa mga kasalukuyang tawag sa iyong device at kontrolin ang mga tawag na ito."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"basahin ang mga mensahe ng cell broadcast"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Binibigyang-daan ang app na magbasa ng mga mensahe ng cell broadcast na natanggap ng iyong device. Inihahatid ang mga alerto ng cell broadcast sa ilang lokasyon upang balaan ka tungkol sa mga emergency na sitwasyon. Maaaring makaabala ang nakakahamak na apps sa performance o pagpapatakbo ng iyong device kapag nakatanggap ng emergency na cell broadcast."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"magbasa ng mga na-subscribe na feed"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 64a150c914af..7751f621e94e 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Uygulamanın hücre yayını mesajları geldiğinde bunları yönlendirmek için hücre yayını modülüne bağlanmasına izin verir. Hücre yayını uyarıları bazı konumlarda acil durumlar hakkında sizi uyarmak için kullanılır. Zararlı uygulamalar acil durum hücre yayını alındığında cihazınızın performansını ve çalışmasını olumsuz etkileyebilir."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Devam eden aramaları yönetme"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Uygulamanın, cihazınızda devam eden aramalarla ilgili bilgileri görüp kontrol etmesine izin verir."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"hücre yayını mesajlarını oku"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Uygulamaya, cihazınız tarafından alınan hücre yayını mesajlarını okuma izni verir. Hücre yayını uyarıları bazı yerlerde acil durumlar konusunda sizi uyarmak için gönderilir. Kötü amaçlı uygulamalar acil hücre yayını alındığında cihazınızın performansına ya da çalışmasına engel olabilir."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"abone olunan yayınları okuma"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 81fe67f86a6a..491956c25112 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -372,6 +372,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Дозволяє додатку зв\'язуватися з модулем Cell Broadcast, щоб переадресувати відповідні вхідні повідомлення. У деяких місцеположеннях сповіщення Cell Broadcast надсилаються для попередження про надзвичайні ситуації. Після повідомлення Cell Broadcast шкідливі додатки можуть перешкоджати роботі вашого пристрою."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Керування поточними дзвінками"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Дає змогу додатку переглядати поточні дзвінки на пристрої та керувати ними."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"читати широкомовні повідомлення мережі"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Дозволяє програмі читати широкомовні повідомлення мережі, отримані пристроєм. Широкомовні сповіщення мережі надсилаються в деяких країнах для попередження про надзвичайні ситуації. Шкідливі програми можуть втручатися у швидкодію чи роботу пристрою під час отримання широкомовного повідомлення мережі про надзвичайну ситуацію."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"читати підписані канали"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 59461b8b39a8..c9346ea25de6 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"سیل کی نشریاتی پیغامات کے موصول ہوتے ہی فارورڈ کرنے کے لیے ایپ کو سیل کے نشریاتی ماڈیول میں پابندی لگانے کی اجازت دیں۔ سیل کی نشریاتی الرٹس آپ کو ایمرجنسی حالات سے مطلع کرنے کیلئے کچھ مقامات میں مہیا کی جاتی ہیں۔ نقصان دہ ایپس کوئی ایمرجنسی سیل براڈ کاسٹ موصول ہونے پر آپ کے آلے کی کارکردگی یا عمل میں مداخلت کر سکتی ہیں۔"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"جاری کالز کا نظم کریں"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"اس سے ایپ کو آپ کے آلے پر جاری کالز کے بارے میں تفصیلات دیکھنے اور ان کالز کو کنٹرول کرنے کی اجازت ملتی ہے۔"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"سیل کے نشریاتی پیغامات پڑھیں"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ایپ کو آپ کے آلے کو موصولہ سیل کے نشریاتی پیغامات پڑھنے کی اجازت دیتا ہے۔ سیل کی نشریاتی الرٹس آپ کو ایمرجنسی حالات سے مطلع کرنے کیلئے کچھ مقامات میں مہیا کی جاتی ہیں۔ نقصان دہ ایپس کوئی ایمرجنسی سیل کا نشریہ موصول ہونے پر آپ کے آلے کی کارکردگی یا عمل میں خلل ڈال سکتی ہیں۔"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"سبسکرائب کردہ فیڈز پڑھیں"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 9388b0f56649..8b52c367e63c 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Qabul qilingan aholini ogohlantirish xabarlarini shu holicha uzatish uchun ilovani aholini ogohlantirish moduliga bogʻlash imkonini beradi. Ilovaga ayrim mamlakatlarda aholini favqulodda vaziyatlarda ogohlantirish uchun yuboriladigan tarqatma xabarlarni oʻqish uchun ruxsat beradi. Zararli dasturlar bunday xabarlar kelayotgan qurilmaning ishlashiga xalaqit qilishi mumkin."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Amaldagi chaqiruvlarni boshqarish"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Ilovaga qurilmangizdagi amaldagi chaqiruv tafsilotlarini koʻrish va uni boshqarish huquqini beradi."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"uyali tarmoq operatori xabarlarini o‘qish"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Ilovaga qurilmangiz tomonidan qabul qilingan uyali tarmoq operatori xabarlarini o‘qish uchun ruxsat beradi. Uyali tarmoq operatorining ogohlantiruvchi xabarlari ba’zi manzillarga favqulodda holatlar haqida ogohlantirish uchun jo‘natiladi. Zararli ilovalar uyali tarmoq orqali favqulodda xabar qabul qilinganda qurilmangizning ish faoliyati yoki amallariga xalaqit qilishi mumkin"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"obunalarni o‘qish"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index b9410c1fefec..3149c18f5a63 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Cho phép ứng dụng liên kết với mô-đun truyền phát trên di động để chuyển tiếp tin nhắn truyền phát trên di động ngay khi nhận được. Ở một số vị trí, thông báo truyền phát trên di động sẽ được gửi nhằm cảnh báo cho bạn về các tình huống khẩn cấp. Các ứng dụng độc hại có thể ảnh hưởng đến hiệu suất hoặc hoạt động của thiết bị khi nhận được tin nhắn truyền phát trên di động lúc khẩn cấp."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Quản lý cuộc gọi đang diễn ra"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Cho phép một ứng dụng xem thông tin chi tiết về các cuộc gọi đang diễn ra trên thiết bị và kiểm soát các cuộc gọi này."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"đọc tin nhắn quảng bá"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Cho phép ứng dụng đọc tin nhắn quảng bá mà thiết bị của bạn nhận được. Tin nhắn quảng bá cảnh báo được gửi ở một số địa điểm nhằm cảnh báo cho bạn về các tình huống khẩn cấp. Các ứng dụng độc hại có thể gây ảnh hưởng đến hiệu suất hoặc hoạt động của thiết bị của bạn khi nhận được tin nhắn quảng bá khẩn cấp."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"đọc nguồn cấp dữ liệu đã đăng ký"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 488dcc63ff0f..168a50e1e818 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"允许应用绑定到小区广播模块,以便及时转发收到的小区广播消息。小区广播消息是在某些地区发送的、用于发布紧急情况警告的提醒信息。恶意应用可能会在您的设备收到紧急小区广播时干扰设备的性能或操作。"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"管理正在进行的通话"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"允许应用查看有关设备上正在进行的通话的详细信息并控制这些通话。"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"读取小区广播消息"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"允许应用读取您的设备收到的小区广播消息。小区广播消息是在某些地区发送的、用于发布紧急情况警告的提醒信息。恶意应用可能会在您收到小区紧急广播时干扰您设备的性能或操作。"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"读取订阅的供稿"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index db117c52c6c8..c6c0997b45cb 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"允許應用程式繫結至區域廣播模組,以在收到區域廣播訊息時轉寄訊息。在某些地點,系統會發出區域廣播通知,提示你有緊急狀況發生。惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的效能或運作。"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"管理正在進行的通話"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"允許應用程式查看裝置上正在進行的通話詳情並控制通話。"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"讀取區域廣播訊息"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"允許應用程式讀取你裝置接收的區域廣播訊息。某些地點會發出區域廣播警報,警告你發生緊急狀況。惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的性能或運作。"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"讀取訂閱的資訊提供"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index e91dc76e2464..3aa79c4c3f8a 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"允許應用程式繫結至區域廣播模組,以便轉送收到的區域廣播訊息。某些地點會發出區域廣播警示,警告你有緊急狀況發生。請注意,惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的效能或運作。"</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"管理進行中的通話"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"允許應用程式查看裝置上進行中通話的詳細資料,並控制這些通話。"</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"讀取區域廣播訊息"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"允許應用程式讀取你裝置收到的區域廣播訊息。某些地點會發出區域廣播警示,警告你有緊急狀況發生。請注意,惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的效能或運作。"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"讀取訂閱資訊提供"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 87a25ca3f660..c18b7d425b38 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -370,6 +370,10 @@ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Ivumela uhlelo lokusebenza ukuthi luboshezelwe kumojuli yokusakaza kweselula ukuze kudluliselwe imilayezo yokusakaza yeselula njengoba itholwa. Izexwayiso zokusakaza kweselula zilethwa kwezinye izindawo ukuze zikuxwayise ngezimo zesimo esiphuthumayo. Izinhlelo zokusebenza ezinobungozi zingaphazamisa ngokusebenza noma ukusetshenziswa kwedivayisi yakho uma ukusakaza kweselula kwesimo esiphuthumayo kwamukelwa."</string> <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Phatha amakholi aqhubekayo"</string> <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Ivumela uhlelo lokusebenza ukubona imininingwane emayelana namakholi aqhubekayo kudivayisi yakho nokulawula lamakholi."</string> + <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) --> + <skip /> + <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) --> + <skip /> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"funda imilayezo yokusakaza yeselula"</string> <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Ivumela uhlelo lokusebenza ukufunda imilayezo yokusakaza yeselula etholwe idivayisi yakho. Izaziso zokusakaza zeselula zilethwa kwezinye izindawo ukukuxwayisa ngezimo ezisheshayo. Izinhlelo zokusebenza ezingalungile zingaphazamisana nokusebenza noma umsebenzi wedivayisi yakho uma ukusakaza kweselula kwesimo esisheshayo kutholwa."</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"funda izifunzo ezikhokhelwayo"</string> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 35276bf8ead2..6884fc0057d9 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -3438,6 +3438,20 @@ <!-- Attributes that can be supplied in an AndroidManifest.xml <code>data</code> tag, a child of the {@link #AndroidManifestIntentFilter intent-filter} tag, describing + a group matching rule consisting of one or more + {@link #AndroidManifestData data} tags that must all match. This + tag can be specified multiple times to create multiple groups that + will be matched in the order they are defined. --> + <declare-styleable name="AndroidManifestUriRelativeFilterGroup" + parent="AndroidManifestIntentFilter"> + <!-- Specify if this group is allow rule or disallow rule. If this + attribute is not specified then it is assumed to be true --> + <attr name="allow" format="boolean"/> + </declare-styleable> + + <!-- Attributes that can be supplied in an AndroidManifest.xml + <code>data</code> tag, a child of the + {@link #AndroidManifestIntentFilter intent-filter} tag, describing the types of data that match. This tag can be specified multiple times to supply multiple data options, as described in the {@link android.content.IntentFilter} class. Note that all such @@ -3445,7 +3459,8 @@ <code><data android:scheme="myscheme" android:host="me.com" /></code> is equivalent to <code><data android:scheme="myscheme" /> <data android:host="me.com" /></code>. --> - <declare-styleable name="AndroidManifestData" parent="AndroidManifestIntentFilter"> + <declare-styleable name="AndroidManifestData" + parent="AndroidManifestIntentFilter AndroidManifestUriRelativeFilterGroup"> <!-- Specify a MIME type that is handled, as per {@link android.content.IntentFilter#addDataType IntentFilter.addDataType()}. @@ -3549,6 +3564,70 @@ IntentFilter.addDataPath()} with {@link android.os.PatternMatcher#PATTERN_SUFFIX}. --> <attr name="pathSuffix" /> + <!-- Specify a URI query that must exactly match, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_LITERAL}. --> + <attr name="query" format="string" /> + <!-- Specify a URI query that must be a prefix to match, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_PREFIX}. --> + <attr name="queryPrefix" format="string" /> + <!-- Specify a URI query that matches a simple pattern, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}. + Note that because '\' is used as an escape character when + reading the string from XML (before it is parsed as a pattern), + you will need to double-escape: for example a literal "*" would + be written as "\\*" and a literal "\" would be written as + "\\\\". This is basically the same as what you would need to + write if constructing the string in Java code. --> + <attr name="queryPattern" format="string" /> + <!-- Specify a URI query that matches an advanced pattern, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}. + Note that because '\' is used as an escape character when + reading the string from XML (before it is parsed as a pattern), + you will need to double-escape: for example a literal "*" would + be written as "\\*" and a literal "\" would be written as + "\\\\". This is basically the same as what you would need to + write if constructing the string in Java code. --> + <attr name="queryAdvancedPattern" format="string" /> + <!-- Specify a URI query that must be a suffix to match, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_SUFFIX}. --> + <attr name="querySuffix" format="string" /> + <!-- Specify a URI fragment that must exactly match, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_LITERAL}. --> + <attr name="fragment" format="string" /> + <!-- Specify a URI fragment that must be a prefix to match, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_PREFIX}. --> + <attr name="fragmentPrefix" format="string" /> + <!-- Specify a URI fragment that matches a simple pattern, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}. + Note that because '\' is used as an escape character when + reading the string from XML (before it is parsed as a pattern), + you will need to double-escape: for example a literal "*" would + be written as "\\*" and a literal "\" would be written as + "\\\\". This is basically the same as what you would need to + write if constructing the string in Java code. --> + <attr name="fragmentPattern" format="string" /> + <!-- Specify a URI fragment that matches an advanced pattern, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}. + Note that because '\' is used as an escape character when + reading the string from XML (before it is parsed as a pattern), + you will need to double-escape: for example a literal "*" would + be written as "\\*" and a literal "\" would be written as + "\\\\". This is basically the same as what you would need to + write if constructing the string in Java code. --> + <attr name="fragmentAdvancedPattern" format="string" /> + <!-- Specify a URI fragment that must be a suffix to match, as a + {@link android.content.UriRelativeFilter UriRelativeFilter} with + {@link android.os.PatternMatcher#PATTERN_SUFFIX}. --> + <attr name="fragmentSuffix" format="string" /> </declare-styleable> <!-- Attributes that can be supplied in an AndroidManifest.xml diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 23c78fdf108e..ebb0d344d7eb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -6908,4 +6908,7 @@ <!-- Defines the minimum interval (in ms) between two input-based user-activity poke events. --> <integer name="config_minMillisBetweenInputUserActivityEvents">100</integer> + + <!-- Name of the starting activity for DisplayCompat host. specific to automotive.--> + <string name="config_defaultDisplayCompatHostActivity" translatable="false"></string> </resources> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index b8fc052a2fa9..830e99ca907b 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -123,6 +123,26 @@ <public name="featureFlag"/> <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") --> <public name="systemUserOnly"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="allow"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="query"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="queryPrefix"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="queryPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="queryAdvancedPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="querySuffix"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentPrefix"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentAdvancedPattern"/> + <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") --> + <public name="fragmentSuffix"/> </staging-public-group> <staging-public-group type="id" first-id="0x01bc0000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index bd3a5e4fe67c..558bae727415 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5307,6 +5307,12 @@ <!-- Content description of the expand button icon in the notification when expanded.--> <string name="expand_button_content_description_expanded">Collapse</string> + <!-- A11y announcement when a view is collapsed. --> + <string name="content_description_collapsed">Collapsed</string> + + <!-- A11y announcement when a view is expanded. --> + <string name="content_description_expanded">Expanded</string> + <!-- Accessibility action description on the expand button. --> <string name="expand_action_accessibility">toggle expansion</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3c1a42feef25..dc2f74bb76fe 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3826,6 +3826,9 @@ <java-symbol type="string" name="expand_button_content_description_collapsed" /> <java-symbol type="string" name="expand_button_content_description_expanded" /> + <java-symbol type="string" name="content_description_collapsed" /> + <java-symbol type="string" name="content_description_expanded" /> + <!-- Colon separated list of package names that should be granted Notification Listener access --> <java-symbol type="string" name="config_defaultListenerAccessPackages" /> diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 3a2e50aa06e8..9bb249999d99 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -34,7 +34,7 @@ http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ --> <!-- Arab Emirates --> - <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" /> + <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" /> <!-- Albania: 5 digits, known short codes listed --> <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" /> @@ -86,7 +86,7 @@ <shortcode country="cn" premium="1066.*" free="1065.*" /> <!-- Colombia: 1-6 digits (not confirmed) --> - <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517" /> + <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289" /> <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU --> <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" /> @@ -104,6 +104,12 @@ <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers --> <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}|4665" /> + <!-- Dominican Republic: 1-6 digits (standard system default, not country specific) --> + <shortcode country="do" pattern="\\d{1,6}" free="912892" /> + + <!-- Ecuador: 1-6 digits (standard system default, not country specific) --> + <shortcode country="ec" pattern="\\d{1,6}" free="466453" /> + <!-- Estonia: short codes 3-5 digits starting with 1, plus premium 7 digit numbers starting with 90, plus EU. http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht --> <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" /> @@ -154,8 +160,8 @@ http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf --> <shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" /> - <!-- Israel: 4 digits, known premium codes listed --> - <shortcode country="il" pattern="\\d{4}" premium="4422|4545" /> + <!-- Israel: 1-5 digits, known premium codes listed --> + <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477|6681" /> <!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU: https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf --> @@ -193,11 +199,14 @@ <shortcode country="mk" pattern="\\d{1,6}" free="129005|122" /> <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed --> - <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453" /> + <shortcode country="mx" pattern="\\d{4,6}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346" /> <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf --> <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" /> + <!-- Namibia: 1-5 digits (standard system default, not country specific) --> + <shortcode country="na" pattern="\\d{1,5}" free="40005" /> + <!-- The Netherlands, 4 digits, known premium codes listed, plus EU --> <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" /> diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java index 63de759282cf..ddf3615d24ed 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java @@ -20,8 +20,6 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; -import android.graphics.Bitmap; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -88,12 +86,6 @@ public final class DefaultRadioTunerTest { return 0; } - @Nullable - @Override - public Bitmap getMetadataImage(int id) { - return null; - } - @Override public boolean startBackgroundScan() { return false; @@ -138,6 +130,16 @@ public final class DefaultRadioTunerTest { } @Test + public void getMetadataImage_forRadioTuner_throwsException() { + UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, + () -> DEFAULT_RADIO_TUNER.getMetadataImage(/* id= */ 1)); + + assertWithMessage("Exception for getting metadata image from default radio tuner") + .that(thrown).hasMessageThat() + .contains("Getting metadata image must be implemented in child classes"); + } + + @Test public void getDynamicProgramList_forRadioTuner_returnsNull() { assertWithMessage("Dynamic program list obtained from default radio tuner") .that(DEFAULT_RADIO_TUNER.getDynamicProgramList(new ProgramList.Filter())).isNull(); diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java index fddfd397d068..b3a0aba0d352 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java @@ -290,13 +290,29 @@ public final class RadioMetadataTest extends ExtendedRadioMockitoTestCase { } @Test - public void getBitmapId_withIllegalKey() { + public void getBitmapId_withIllegalKey_fails() { String illegalKey = RadioMetadata.METADATA_KEY_ARTIST; RadioMetadata metadata = mBuilder.putInt(RadioMetadata.METADATA_KEY_ART, INT_KEY_VALUE) .build(); - mExpect.withMessage("Bitmap id value with non-bitmap-id-type key %s", illegalKey) - .that(metadata.getBitmapId(illegalKey)).isEqualTo(0); + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> { + metadata.getBitmapId(illegalKey); + }); + + mExpect.withMessage("Exception for getting string array for non-bitmap-id type key %s", + illegalKey).that(thrown).hasMessageThat().contains("bitmap key"); + } + + @Test + public void getBitmapId_withNullKey_fails() { + RadioMetadata metadata = mBuilder.build(); + + NullPointerException thrown = assertThrows(NullPointerException.class, () -> { + metadata.getBitmapId(/* key= */ null); + }); + + mExpect.withMessage("Exception for getting bitmap identifier with null key") + .that(thrown).hasMessageThat().contains("can not be null"); } @Test diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java index 4cda26de2906..5aace81696cf 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java @@ -514,6 +514,26 @@ public final class TunerAdapterTest { } @Test + public void getMetadataImage_withImageIdUnavailable_fails() throws Exception { + int nonExistImageId = 2; + when(mTunerMock.getImage(nonExistImageId)).thenReturn(null); + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> mRadioTuner.getMetadataImage(nonExistImageId)); + + assertWithMessage("Exception for getting metadata image with non-existing id") + .that(thrown).hasMessageThat().contains("is not available"); + } + + @Test + public void getMetadataImage_withInvalidId_fails() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> mRadioTuner.getMetadataImage(/* id= */ 0)); + + assertWithMessage("Exception for getting metadata image for id 0").that(thrown) + .hasMessageThat().contains("Invalid metadata image id 0"); + } + + @Test public void getMetadataImage_whenServiceDied_fails() throws Exception { when(mTunerMock.getImage(anyInt())).thenThrow(new RemoteException()); diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java index e7b5dff60110..93c2e0e40593 100644 --- a/core/tests/coretests/src/android/os/BundleTest.java +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -121,6 +122,14 @@ public class BundleTest { } @Test + public void testEmpty() throws Exception { + assertNotNull(Bundle.EMPTY); + assertEquals(0, Bundle.EMPTY.size()); + + new Bundle(Bundle.EMPTY); + } + + @Test @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) public void testCreateFromParcel() throws Exception { boolean withFd; diff --git a/core/tests/coretests/src/android/os/TestLooperManagerTest.java b/core/tests/coretests/src/android/os/TestLooperManagerTest.java new file mode 100644 index 000000000000..5959444e49cc --- /dev/null +++ b/core/tests/coretests/src/android/os/TestLooperManagerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class TestLooperManagerTest { + private static final String TAG = "TestLooperManagerTest"; + + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Test + public void testMainThread() throws Exception { + doTest(Looper.getMainLooper()); + } + + @Test + public void testCustomThread() throws Exception { + final HandlerThread thread = new HandlerThread(TAG); + thread.start(); + doTest(thread.getLooper()); + } + + private void doTest(Looper looper) throws Exception { + final TestLooperManager tlm = + InstrumentationRegistry.getInstrumentation().acquireLooperManager(looper); + + final Handler handler = new Handler(looper); + final CountDownLatch latch = new CountDownLatch(1); + + assertFalse(tlm.hasMessages(handler, null, 42)); + + handler.sendEmptyMessage(42); + handler.post(() -> { + latch.countDown(); + }); + assertTrue(tlm.hasMessages(handler, null, 42)); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + + final Message first = tlm.next(); + assertEquals(42, first.what); + assertNull(first.callback); + tlm.execute(first); + assertFalse(tlm.hasMessages(handler, null, 42)); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + tlm.recycle(first); + + final Message second = tlm.next(); + assertNotNull(second.callback); + tlm.execute(second); + assertFalse(tlm.hasMessages(handler, null, 42)); + assertTrue(latch.await(100, TimeUnit.MILLISECONDS)); + tlm.recycle(second); + + tlm.release(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index 75b0d4a159d9..145aa60dc495 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -21,7 +21,6 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHO import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; @@ -83,6 +82,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.util.test.FakeSettingsProvider; import org.junit.AfterClass; @@ -726,14 +726,14 @@ public class AccessibilityShortcutControllerTest { private void configureNoShortcutService() throws Exception { when(mAccessibilityManagerService - .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) + .getAccessibilityShortcutTargets(ShortcutConstants.UserShortcutType.HARDWARE)) .thenReturn(Collections.emptyList()); Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, ""); } private void configureValidShortcutService() throws Exception { when(mAccessibilityManagerService - .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) + .getAccessibilityShortcutTargets(ShortcutConstants.UserShortcutType.HARDWARE)) .thenReturn(Collections.singletonList(SERVICE_NAME_STRING)); Settings.Secure.putString( mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING); @@ -744,7 +744,7 @@ public class AccessibilityShortcutControllerTest { (ComponentName) AccessibilityShortcutController.getFrameworkShortcutFeaturesMap() .keySet().toArray()[0]; when(mAccessibilityManagerService - .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) + .getAccessibilityShortcutTargets(ShortcutConstants.UserShortcutType.HARDWARE)) .thenReturn(Collections.singletonList(featureComponentName.flattenToString())); Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, featureComponentName.flattenToString()); @@ -806,7 +806,7 @@ public class AccessibilityShortcutControllerTest { private void configureDefaultAccessibilityService() throws Exception { when(mAccessibilityManagerService - .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) + .getAccessibilityShortcutTargets(ShortcutConstants.UserShortcutType.HARDWARE)) .thenReturn(Collections.singletonList(SERVICE_NAME_STRING)); when(mResources.getString(R.string.config_defaultAccessibilityService)).thenReturn( diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java index 69b6a9b7aa87..2ea044ccfb52 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java @@ -39,6 +39,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.accessibility.TestUtils; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; @@ -99,7 +100,7 @@ public class InvisibleToggleAccessibilityServiceTargetTest { mSut = new InvisibleToggleAccessibilityServiceTarget( mContextSpy, - AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY, accessibilityServiceInfo); + ShortcutConstants.UserShortcutType.HARDWARE, accessibilityServiceInfo); } @Test diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 62c9e16f753a..28734283e9b7 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -642,5 +642,6 @@ applications that come with the platform <privapp-permissions package="com.android.devicediagnostics"> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + <permission name="android.permission.BATTERY_STATS"/> </privapp-permissions> </permissions> diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml index 02e032b86443..1bd182f96bde 100644 --- a/data/fonts/font_fallback.xml +++ b/data/fonts/font_fallback.xml @@ -730,6 +730,16 @@ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc </font> </family> + <family lang="ja"> + <font weight="400" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="700" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> <family lang="ko"> <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular"> NotoSansCJK-Regular.ttc diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml index 70474bae11e9..75bc74ebb553 100644 --- a/data/fonts/font_fallback_cjkvf.xml +++ b/data/fonts/font_fallback_cjkvf.xml @@ -829,6 +829,16 @@ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc </font> </family> + <family lang="ja"> + <font weight="400" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="700" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> <family lang="ko"> <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> NotoSansCJK-Regular.ttc diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 9320c144d2ec..b23f00554bdb 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -1432,6 +1432,16 @@ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc </font> </family> + <family lang="ja"> + <font weight="400" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="700" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> <family lang="ko"> <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular"> NotoSansCJK-Regular.ttc diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml index 8d91eddd807d..1ab71ae270eb 100644 --- a/data/fonts/fonts_cjkvf.xml +++ b/data/fonts/fonts_cjkvf.xml @@ -1531,6 +1531,16 @@ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc </font> </family> + <family lang="ja"> + <font weight="400" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="700" style="normal"> + NotoSerifHentaigana-EL.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> <family lang="ko"> <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> NotoSansCJK-Regular.ttc diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java index b21bf11088e2..7d55928aa656 100644 --- a/graphics/java/android/graphics/text/LineBreakConfig.java +++ b/graphics/java/android/graphics/text/LineBreakConfig.java @@ -176,6 +176,9 @@ public final class LineBreakConfig implements Parcelable { * - If at least one locale in the locale list contains Japanese script, this option is * equivalent to {@link #LINE_BREAK_STYLE_STRICT}. * - Otherwise, this option is equivalent to {@link #LINE_BREAK_STYLE_NONE}. + * + * <p> + * Note: future versions may have special line breaking style rules for other locales. */ @FlaggedApi(FLAG_WORD_STYLE_AUTO) public static final int LINE_BREAK_STYLE_AUTO = 5; @@ -249,6 +252,9 @@ public final class LineBreakConfig implements Parcelable { * option is equivalent to {@link #LINE_BREAK_WORD_STYLE_PHRASE} if the result of its line * count is less than 5 lines. * - Otherwise, this option is equivalent to {@link #LINE_BREAK_WORD_STYLE_NONE}. + * + * <p> + * Note: future versions may have special line breaking word style rules for other locales. */ @FlaggedApi(FLAG_WORD_STYLE_AUTO) public static final int LINE_BREAK_WORD_STYLE_AUTO = 2; diff --git a/keystore/java/android/security/AndroidProtectedConfirmation.java b/keystore/java/android/security/AndroidProtectedConfirmation.java index dfe485ac8274..268e0a54053b 100644 --- a/keystore/java/android/security/AndroidProtectedConfirmation.java +++ b/keystore/java/android/security/AndroidProtectedConfirmation.java @@ -59,6 +59,10 @@ public class AndroidProtectedConfirmation { /** * Requests keystore call into the confirmationui HAL to display a prompt. + * @deprecated Android Protected Confirmation had a low adoption rate among Android device + * makers and developers alike. Given the lack of devices supporting the + * feature, it is deprecated. Developers can use auth-bound Keystore keys + * as a partial replacement. * * @param listener the binder to use for callbacks. * @param promptText the prompt to display. @@ -68,6 +72,7 @@ public class AndroidProtectedConfirmation { * @return one of the {@code CONFIRMATIONUI_*} constants, for * example {@code KeyStore.CONFIRMATIONUI_OK}. */ + @Deprecated public int presentConfirmationPrompt(IConfirmationCallback listener, String promptText, byte[] extraData, String locale, int uiOptionsAsFlags) { try { @@ -84,11 +89,16 @@ public class AndroidProtectedConfirmation { /** * Requests keystore call into the confirmationui HAL to cancel displaying a prompt. + * @deprecated Android Protected Confirmation had a low adoption rate among Android device + * makers and developers alike. Given the lack of devices supporting the + * feature, it is deprecated. Developers can use auth-bound Keystore keys + * as a partial replacement. * * @param listener the binder passed to the {@link #presentConfirmationPrompt} method. * @return one of the {@code CONFIRMATIONUI_*} constants, for * example {@code KeyStore.CONFIRMATIONUI_OK}. */ + @Deprecated public int cancelConfirmationPrompt(IConfirmationCallback listener) { try { getService().cancelPrompt(listener); @@ -103,9 +113,14 @@ public class AndroidProtectedConfirmation { /** * Requests keystore to check if the confirmationui HAL is available. + * @deprecated Android Protected Confirmation had a low adoption rate among Android device + * makers and developers alike. Given the lack of devices supporting the + * feature, it is deprecated. Developers can use auth-bound Keystore keys + * as a partial replacement. * * @return whether the confirmationUI HAL is available. */ + @Deprecated public boolean isConfirmationPromptSupported() { try { return getService().isSupported(); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 9c05a3a768a0..83ddfc5cf1a1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -109,13 +109,29 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } } + // For curve 25519, KeyMint uses the KM_ALGORITHM_EC constant, but in the Java layer we need + // to distinguish between Curve 25519 and other EC algorithms, so we use a different constant + // with a value that is outside the range of the enum used for KeyMint algorithms. + private static final int ALGORITHM_XDH = KeymasterDefs.KM_ALGORITHM_EC + 1200; + private static final int ALGORITHM_ED25519 = ALGORITHM_XDH + 1; + /** - * XDH represents Curve 25519 providers. + * XDH represents Curve 25519 agreement key provider. */ public static class XDH extends AndroidKeyStoreKeyPairGeneratorSpi { // XDH is treated as EC. public XDH() { - super(KeymasterDefs.KM_ALGORITHM_EC); + super(ALGORITHM_XDH); + } + } + + /** + * ED25519 represents Curve 25519 signing key provider. + */ + public static class ED25519 extends AndroidKeyStoreKeyPairGeneratorSpi { + // ED25519 is treated as EC. + public ED25519() { + super(ALGORITHM_ED25519); } } @@ -241,7 +257,9 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato KeyGenParameterSpec spec; boolean encryptionAtRestRequired = false; - int keymasterAlgorithm = mOriginalKeymasterAlgorithm; + int keymasterAlgorithm = (mOriginalKeymasterAlgorithm == ALGORITHM_XDH + || mOriginalKeymasterAlgorithm == ALGORITHM_ED25519) + ? KeymasterDefs.KM_ALGORITHM_EC : mOriginalKeymasterAlgorithm; if (params instanceof KeyGenParameterSpec) { spec = (KeyGenParameterSpec) params; } else if (params instanceof KeyPairGeneratorSpec) { @@ -610,6 +628,15 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato if (algSpecificSpec instanceof ECGenParameterSpec) { ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec; mEcCurveName = ecSpec.getName(); + if (mOriginalKeymasterAlgorithm == ALGORITHM_XDH + && !mEcCurveName.equalsIgnoreCase("x25519")) { + throw new InvalidAlgorithmParameterException("XDH algorithm only supports" + + " x25519 curve."); + } else if (mOriginalKeymasterAlgorithm == ALGORITHM_ED25519 + && !mEcCurveName.equalsIgnoreCase("ed25519")) { + throw new InvalidAlgorithmParameterException("Ed25519 algorithm only" + + " supports ed25519 curve."); + } final Integer ecSpecKeySizeBits = SUPPORTED_EC_CURVE_NAME_TO_SIZE.get( mEcCurveName.toLowerCase(Locale.US)); if (ecSpecKeySizeBits == null) { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 11278e84ceaa..d204f13d4d78 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -86,11 +86,14 @@ public class AndroidKeyStoreProvider extends Provider { put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); put("KeyPairGenerator.XDH", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$XDH"); + put("KeyPairGenerator.ED25519", PACKAGE_NAME + + ".AndroidKeyStoreKeyPairGeneratorSpi$ED25519"); // java.security.KeyFactory putKeyFactoryImpl("EC"); putKeyFactoryImpl("RSA"); putKeyFactoryImpl("XDH"); + putKeyFactoryImpl("ED25519"); // javax.crypto.KeyGenerator put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index 9b84a48cdbda..6e704f35fb22 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -356,7 +356,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } if (featureRect.left == 0 && featureRect.width() != windowConfiguration.getBounds().width()) { - Log.wtf(TAG, "Horizontal FoldingFeature must have full width." + Log.w(TAG, "Horizontal FoldingFeature must have full width." + " BaseFeatureRect: " + baseFeature.getRect() + ", FeatureRect: " + featureRect + ", WindowConfiguration: " + windowConfiguration); @@ -364,7 +364,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } if (featureRect.top == 0 && featureRect.height() != windowConfiguration.getBounds().height()) { - Log.wtf(TAG, "Vertical FoldingFeature must have full height." + Log.w(TAG, "Vertical FoldingFeature must have full height." + " BaseFeatureRect: " + baseFeature.getRect() + ", FeatureRect: " + featureRect + ", WindowConfiguration: " + windowConfiguration); diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt index 4c76168cdeaa..398fd554f030 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt @@ -22,7 +22,6 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.wm.shell.taskview.TaskView -import com.android.wm.shell.taskview.TaskViewTaskController import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.MoreExecutors.directExecutor @@ -30,6 +29,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidJUnit4::class) @@ -37,10 +38,11 @@ class BubbleTaskViewTest { private lateinit var bubbleTaskView: BubbleTaskView private val context = ApplicationProvider.getApplicationContext<Context>() + private lateinit var taskView: TaskView @Before fun setUp() { - val taskView = TaskView(context, mock<TaskViewTaskController>()) + taskView = mock() bubbleTaskView = BubbleTaskView(taskView, directExecutor()) } @@ -72,4 +74,19 @@ class BubbleTaskViewTest { assertThat(actualTaskId).isEqualTo(123) assertThat(actualComponentName).isEqualTo(componentName) } + + @Test + fun cleanup_invalidTaskId_doesNotRemoveTask() { + bubbleTaskView.cleanup() + verify(taskView, never()).removeTask() + } + + @Test + fun cleanup_validTaskId_removesTask() { + val componentName = ComponentName(context, "TestClass") + bubbleTaskView.listener.onTaskCreated(123, componentName) + + bubbleTaskView.cleanup() + verify(taskView).removeTask() + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 87c8f526c6fa..f32f030ff06e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -51,6 +51,8 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.common.bubbles.BubbleInfo; +import com.android.wm.shell.taskview.TaskView; +import com.android.wm.shell.taskview.TaskViewTaskController; import java.io.PrintWriter; import java.util.List; @@ -105,6 +107,8 @@ public class Bubble implements BubbleViewProvider { private BubbleExpandedView mExpandedView; @Nullable private BubbleBarExpandedView mBubbleBarExpandedView; + @Nullable + private BubbleTaskView mBubbleTaskView; private BubbleViewInfoTask mInflationTask; private boolean mInflateSynchronously; @@ -394,6 +398,21 @@ public class Bubble implements BubbleViewProvider { } /** + * Returns the existing {@link #mBubbleTaskView} if it's not {@code null}. Otherwise a new + * instance of {@link BubbleTaskView} is created. + */ + public BubbleTaskView getOrCreateBubbleTaskView(Context context, BubbleController controller) { + if (mBubbleTaskView == null) { + TaskViewTaskController taskViewTaskController = new TaskViewTaskController(context, + controller.getTaskOrganizer(), + controller.getTaskViewTransitions(), controller.getSyncTransactionQueue()); + TaskView taskView = new TaskView(context, taskViewTaskController); + mBubbleTaskView = new BubbleTaskView(taskView, controller.getMainExecutor()); + } + return mBubbleTaskView; + } + + /** * @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise. */ String getShortcutId() { @@ -415,6 +434,10 @@ public class Bubble implements BubbleViewProvider { * the bubble. */ void cleanupExpandedView() { + cleanupExpandedView(true); + } + + private void cleanupExpandedView(boolean cleanupTaskView) { if (mExpandedView != null) { mExpandedView.cleanUpExpandedState(); mExpandedView = null; @@ -423,17 +446,37 @@ public class Bubble implements BubbleViewProvider { mBubbleBarExpandedView.cleanUpExpandedState(); mBubbleBarExpandedView = null; } + if (cleanupTaskView) { + cleanupTaskView(); + } if (mIntent != null) { mIntent.unregisterCancelListener(mIntentCancelListener); } mIntentActive = false; } + private void cleanupTaskView() { + if (mBubbleTaskView != null) { + mBubbleTaskView.cleanup(); + mBubbleTaskView = null; + } + } + /** * Call when all the views should be removed/cleaned up. */ void cleanupViews() { - cleanupExpandedView(); + cleanupViews(true); + } + + /** + * Call when all the views should be removed/cleaned up. + * + * <p>If we're switching between bar and floating modes, pass {@code false} on + * {@code cleanupTaskView} to avoid recreating it in the new mode. + */ + void cleanupViews(boolean cleanupTaskView) { + cleanupExpandedView(cleanupTaskView); mIconView = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index a5f7880cfd41..945b8ac59b98 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1366,8 +1366,9 @@ public class BubbleController implements ConfigurationChangeListener, mStackView.resetOverflowView(); mStackView.removeAllViews(); } - // cleanup existing bubble views so they can be recreated later if needed. - mBubbleData.getBubbles().forEach(Bubble::cleanupViews); + // cleanup existing bubble views so they can be recreated later if needed, but retain + // TaskView. + mBubbleData.getBubbles().forEach(b -> b.cleanupViews(/* cleanupTaskView= */ false)); // remove the current bubble container from window manager, null it out, and create a new // container based on the current mode. @@ -1478,7 +1479,6 @@ public class BubbleController implements ConfigurationChangeListener, * <p> * Must be called from the main thread. */ - @VisibleForTesting @MainThread public void removeBubble(String key, int reason) { if (mBubbleData.hasAnyBubbleWithKey(key)) { @@ -1486,36 +1486,6 @@ public class BubbleController implements ConfigurationChangeListener, } } - // TODO(b/316358859): remove this method after task views are shared across modes - /** - * Removes the bubble with the given key after task removal, unless the task was removed as - * a result of mode switching, in which case, the bubble isn't removed because it will be - * re-inflated for the new mode. - */ - @MainThread - public void removeFloatingBubbleAfterTaskRemoval(String key, int reason) { - // if we're floating remove the bubble. otherwise, we're here because the task was removed - // after switching modes. See b/316358859 - if (!isShowingAsBubbleBar()) { - removeBubble(key, reason); - } - } - - // TODO(b/316358859): remove this method after task views are shared across modes - /** - * Removes the bubble with the given key after task removal, unless the task was removed as - * a result of mode switching, in which case, the bubble isn't removed because it will be - * re-inflated for the new mode. - */ - @MainThread - public void removeBarBubbleAfterTaskRemoval(String key, int reason) { - // if we're showing as bubble bar remove the bubble. otherwise, we're here because the task - // was removed after switching modes. See b/316358859 - if (isShowingAsBubbleBar()) { - removeBubble(key, reason); - } - } - /** * Removes all the bubbles. * <p> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 9f7d0ac9bafe..efc4d8b95f4f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -27,12 +27,10 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPAND import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT; -import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.app.ActivityOptions; -import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -49,7 +47,6 @@ import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; -import android.os.RemoteException; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.IntProperty; @@ -311,8 +308,7 @@ public class BubbleExpandedView extends LinearLayout { + " bubble=" + getBubbleKey()); } if (mBubble != null) { - mController.removeFloatingBubbleAfterTaskRemoval( - mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); + mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); } if (mTaskView != null) { // Release the surface @@ -1105,32 +1101,11 @@ public class BubbleExpandedView extends LinearLayout { return ((LinearLayout.LayoutParams) mManageButton.getLayoutParams()).getMarginStart(); } - /** - * Cleans up anything related to the task. The TaskView itself is released after the task - * has been removed. - * - * If this view should be reused after this method is called, then - * {@link #initialize(BubbleController, BubbleStackView, boolean, BubbleTaskView)} - * must be invoked first. - */ + /** Hide the task view. */ public void cleanUpExpandedState() { if (DEBUG_BUBBLE_EXPANDED_VIEW) { Log.d(TAG, "cleanUpExpandedState: bubble=" + getBubbleKey() + " task=" + mTaskId); } - if (getTaskId() != INVALID_TASK_ID) { - // Ensure the task is removed from WM - if (ENABLE_SHELL_TRANSITIONS) { - if (mTaskView != null) { - mTaskView.removeTask(); - } - } else { - try { - ActivityTaskManager.getService().removeTask(getTaskId()); - } catch (RemoteException e) { - Log.w(TAG, e.getMessage()); - } - } - } if (mTaskView != null) { mTaskView.setVisibility(GONE); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt index 2fcd133c7b20..65f8e48eb822 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt @@ -16,10 +16,14 @@ package com.android.wm.shell.bubbles +import android.app.ActivityTaskManager import android.app.ActivityTaskManager.INVALID_TASK_ID import android.content.ComponentName +import android.os.RemoteException +import android.util.Log import androidx.annotation.VisibleForTesting import com.android.wm.shell.taskview.TaskView +import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS import java.util.concurrent.Executor /** @@ -78,4 +82,28 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) { init { taskView.setListener(executor, listener) } + + /** + * Removes the [TaskView] from window manager. + * + * This should be called after all other cleanup animations have finished. + */ + fun cleanup() { + if (taskId != INVALID_TASK_ID) { + // Ensure the task is removed from WM + if (ENABLE_SHELL_TRANSITIONS) { + taskView.removeTask() + } else { + try { + ActivityTaskManager.getService().removeTask(taskId) + } catch (e: RemoteException) { + Log.w(TAG, e.message ?: "") + } + } + } + } + + private companion object { + const val TAG = "BubbleTaskView" + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java index 5855a81333d4..5fc67d7b5f00 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java @@ -29,7 +29,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Rect; -import android.os.RemoteException; import android.util.Log; import android.view.View; @@ -183,8 +182,11 @@ public class BubbleTaskViewHelper { + " bubble=" + getBubbleKey()); } if (mBubble != null) { - mController.removeBarBubbleAfterTaskRemoval( - mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); + mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED); + } + if (mTaskView != null) { + mTaskView.release(); + mTaskView = null; } } @@ -228,24 +230,6 @@ public class BubbleTaskViewHelper { return false; } - /** Cleans up anything related to the task and {@code TaskView}. */ - public void cleanUpTaskView() { - if (DEBUG_BUBBLE_EXPANDED_VIEW) { - Log.d(TAG, "cleanUpExpandedState: bubble=" + getBubbleKey() + " task=" + mTaskId); - } - if (mTaskId != INVALID_TASK_ID) { - try { - ActivityTaskManager.getService().removeTask(mTaskId); - } catch (RemoteException e) { - Log.w(TAG, e.getMessage()); - } - } - if (mTaskView != null) { - mTaskView.release(); - mTaskView = null; - } - } - /** Returns the bubble key associated with this view. */ @Nullable public String getBubbleKey() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index c3d899e7dac7..5fc10a9f6b69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -46,8 +46,6 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; -import com.android.wm.shell.taskview.TaskView; -import com.android.wm.shell.taskview.TaskViewTaskController; import java.lang.ref.WeakReference; import java.util.Objects; @@ -175,7 +173,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask BubbleViewInfo info = new BubbleViewInfo(); if (!skipInflation && !b.isInflated()) { - BubbleTaskView bubbleTaskView = createBubbleTaskView(c, controller); + BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(c, controller); LayoutInflater inflater = LayoutInflater.from(c); info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); @@ -205,7 +203,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask R.layout.bubble_view, stackView, false /* attachToRoot */); info.imageView.initialize(controller.getPositioner()); - BubbleTaskView bubbleTaskView = createBubbleTaskView(c, controller); + BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(c, controller); info.expandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); info.expandedView.initialize( @@ -225,15 +223,6 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask } return info; } - - private static BubbleTaskView createBubbleTaskView( - Context context, BubbleController controller) { - TaskViewTaskController taskViewTaskController = new TaskViewTaskController(context, - controller.getTaskOrganizer(), - controller.getTaskViewTransitions(), controller.getSyncTransactionQueue()); - TaskView taskView = new TaskView(context, taskViewTaskController); - return new BubbleTaskView(taskView, controller.getMainExecutor()); - } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 3cf23ac114ee..00d683e861e0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -272,7 +272,6 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView if (mTaskView != null) { removeView(mTaskView); } - mBubbleTaskViewHelper.cleanUpTaskView(); } mMenuViewController.hideMenu(false /* animated */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMenuController.java index 0775f5279e31..2f1189a8a984 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMenuController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.pip; +package com.android.wm.shell.common.pip; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; @@ -33,12 +33,13 @@ import android.view.SurfaceControl; import android.view.WindowManager; import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; import java.util.List; /** - * Interface to allow {@link com.android.wm.shell.pip.PipTaskOrganizer} to call into - * PiP menu when certain events happen (task appear/vanish, PiP move, etc.) + * Interface to interact with PiP menu when certain events happen + * (task appear/vanish, PiP move, etc.). */ public interface PipMenuController { @@ -52,15 +53,15 @@ public interface PipMenuController { float ALPHA_NO_CHANGE = -1f; /** - * Called when - * {@link PipTaskOrganizer#onTaskAppeared(RunningTaskInfo, SurfaceControl)} + * Called when out implementation of + * {@link ShellTaskOrganizer.TaskListener#onTaskAppeared(RunningTaskInfo, SurfaceControl)} * is called. */ void attach(SurfaceControl leash); /** - * Called when - * {@link PipTaskOrganizer#onTaskVanished(RunningTaskInfo)} is called. + * Called when our implementation of + * {@link ShellTaskOrganizer.TaskListener#onTaskVanished(RunningTaskInfo)} is called. */ void detach(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index 7b98fa6523cb..8eecf1c58db0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -18,18 +18,23 @@ package com.android.wm.shell.dagger.pip; import android.annotation.NonNull; import android.content.Context; +import android.os.Handler; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; +import com.android.wm.shell.common.pip.PipMediaController; +import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; +import com.android.wm.shell.pip2.phone.PhonePipMenuController; import com.android.wm.shell.pip2.phone.PipController; import com.android.wm.shell.pip2.phone.PipScheduler; import com.android.wm.shell.pip2.phone.PipTransition; @@ -86,4 +91,16 @@ public abstract class Pip2Module { @ShellMainThread ShellExecutor mainExecutor) { return new PipScheduler(context, pipBoundsState, mainExecutor); } + + @WMSingleton + @Provides + static PhonePipMenuController providePipPhoneMenuController(Context context, + PipBoundsState pipBoundsState, PipMediaController pipMediaController, + SystemWindows systemWindows, + PipUiEventLogger pipUiEventLogger, + @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler) { + return new PhonePipMenuController(context, pipBoundsState, pipMediaController, + systemWindows, pipUiEventLogger, mainExecutor, mainHandler); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index da1ca8d57940..6250fc5820aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -17,6 +17,8 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE; @@ -86,7 +88,6 @@ public class DesktopModeVisualIndicator { mTaskSurface = taskSurface; mRootTdaOrganizer = taskDisplayAreaOrganizer; mCurrentType = IndicatorType.NO_INDICATOR; - createView(); } /** @@ -127,34 +128,15 @@ public class DesktopModeVisualIndicator { mView = new View(mContext); final SurfaceControl.Builder builder = new SurfaceControl.Builder(); mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder); - String description; - switch (mCurrentType) { - case TO_DESKTOP_INDICATOR: - description = "Desktop indicator"; - break; - case TO_FULLSCREEN_INDICATOR: - description = "Fullscreen indicator"; - break; - case TO_SPLIT_LEFT_INDICATOR: - description = "Split Left indicator"; - break; - case TO_SPLIT_RIGHT_INDICATOR: - description = "Split Right indicator"; - break; - default: - description = "Invalid indicator"; - break; - } mLeash = builder - .setName(description) + .setName("Desktop Mode Visual Indicator") .setContainerLayer() .build(); t.show(mLeash); final WindowManager.LayoutParams lp = - new WindowManager.LayoutParams(screenWidth, screenHeight, - WindowManager.LayoutParams.TYPE_APPLICATION, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); - lp.setTitle(description + " for Task=" + mTaskInfo.taskId); + new WindowManager.LayoutParams(screenWidth, screenHeight, TYPE_APPLICATION, + FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); + lp.setTitle("Desktop Mode Visual Indicator"); lp.setTrustedOverlay(); final WindowlessWindowManager windowManager = new WindowlessWindowManager( mTaskInfo.configuration, mLeash, @@ -201,6 +183,9 @@ public class DesktopModeVisualIndicator { */ private void transitionIndicator(IndicatorType newType) { if (mCurrentType == newType) return; + if (mView == null) { + createView(); + } if (mCurrentType == IndicatorType.NO_INDICATOR) { fadeInIndicator(newType); } else if (newType == IndicatorType.NO_INDICATOR) { 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 4f5c39a60120..28c06a46c516 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 @@ -919,22 +919,27 @@ class DesktopTasksController( } if (taskBounds.top <= transitionAreaHeight) { moveToFullscreenWithAnimation(taskInfo, position) + return } if (inputCoordinate.x <= transitionAreaWidth) { releaseVisualIndicator() - var wct = WindowContainerTransaction() + val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_TOP_OR_LEFT, taskBounds) + return } if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() ?.minus(transitionAreaWidth) ?: return)) { releaseVisualIndicator() - var wct = WindowContainerTransaction() + val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds) + return } + // A freeform drag-move ended, remove the indicator immediately. + releaseVisualIndicator() } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt index c3a82ce258df..731fec7899ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt @@ -239,7 +239,7 @@ class DragToDesktopTransitionHandler( show(change.leash) } } else if (TransitionInfo.isIndependent(change, info)) { - // Root. + // Root(s). when (state) { is TransitionState.FromSplit -> { state.splitRootChange = change @@ -256,6 +256,9 @@ class DragToDesktopTransitionHandler( } } is TransitionState.FromFullscreen -> { + // Most of the time we expect one change/task here, which should be the + // same that initiated the drag and that should be layered on top of + // everything. if (change.taskInfo?.taskId == state.draggedTaskId) { state.draggedTaskChange = change val bounds = change.endAbsBounds @@ -265,7 +268,18 @@ class DragToDesktopTransitionHandler( show(change.leash) } } else { - throw IllegalStateException("Expected root to be dragged task") + // It's possible to see an additional change that isn't the dragged + // task when the dragged task is translucent and so the task behind it + // is included in the transition since it was visible and is now being + // occluded by the Home task. Just layer it at the bottom and save it + // in case we need to restore order if the drag is cancelled. + state.otherRootChanges.add(change) + val bounds = change.endAbsBounds + startTransaction.apply { + setLayer(change.leash, appLayers - i) + setWindowCrop(change.leash, bounds.width(), bounds.height()) + show(change.leash) + } } } } @@ -515,8 +529,18 @@ class DragToDesktopTransitionHandler( val wct = WindowContainerTransaction() when (state) { is TransitionState.FromFullscreen -> { + // There may have been tasks sent behind home that are not the dragged task (like + // when the dragged task is translucent and that makes the task behind it visible). + // Restore the order of those first. + state.otherRootChanges.mapNotNull { it.container }.forEach { wc -> + // TODO(b/322852244): investigate why even though these "other" tasks are + // reordered in front of home and behind the translucent dragged task, its + // surface is not visible on screen. + wct.reorder(wc, true /* toTop */) + } val wc = state.draggedTaskChange?.container ?: error("Dragged task should be non-null before cancelling") + // Then the dragged task a the very top. wct.reorder(wc, true /* toTop */) } is TransitionState.FromSplit -> { @@ -574,6 +598,7 @@ class DragToDesktopTransitionHandler( override var draggedTaskChange: Change? = null, override var cancelled: Boolean = false, override var startAborted: Boolean = false, + var otherRootChanges: MutableList<Change> = mutableListOf() ) : TransitionState() data class FromSplit( override val draggedTaskId: Int, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 07b8f11458be..52a06e0bbe7f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -86,6 +86,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; +import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.pip.phone.PipMotionHelper; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 8e375a9ef5ee..e7392662bdf1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -68,6 +68,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; +import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 0e7073688ec4..d1fd207c4a66 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -40,6 +40,7 @@ import androidx.annotation.NonNull; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index 760652625f9e..d8e8b587004a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -41,8 +41,8 @@ import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipMediaController; import com.android.wm.shell.common.pip.PipMediaController.ActionListener; +import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUiEventLogger; -import com.android.wm.shell.pip.PipMenuController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index c6803f7beebd..843c84a06f8c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -40,7 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.SystemWindows; -import com.android.wm.shell.pip.PipMenuController; +import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.List; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java index 21223c9ac362..cac63eb2a2ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java @@ -28,10 +28,10 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; +import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.pip.PipMenuController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java index 571c839adf11..d16a692dbd0a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java @@ -25,10 +25,10 @@ import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.transitTypeToString; +import static com.android.wm.shell.common.pip.PipMenuController.ALPHA_NO_CHANGE; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; -import static com.android.wm.shell.pip.PipMenuController.ALPHA_NO_CHANGE; import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP; import static com.android.wm.shell.pip.PipTransitionState.ENTERING_PIP; import static com.android.wm.shell.pip.PipTransitionState.EXITING_PIP; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java new file mode 100644 index 000000000000..24077a35d41c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip2; + +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.Choreographer; +import android.view.SurfaceControl; + +import com.android.wm.shell.R; +import com.android.wm.shell.transition.Transitions; + +/** + * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition. + */ +public class PipSurfaceTransactionHelper { + /** for {@link #scale(SurfaceControl.Transaction, SurfaceControl, Rect, Rect)} operation */ + private final Matrix mTmpTransform = new Matrix(); + private final float[] mTmpFloat9 = new float[9]; + private final RectF mTmpSourceRectF = new RectF(); + private final RectF mTmpDestinationRectF = new RectF(); + private final Rect mTmpDestinationRect = new Rect(); + + private int mCornerRadius; + private int mShadowRadius; + + public PipSurfaceTransactionHelper(Context context) { + onDensityOrFontScaleChanged(context); + } + + /** + * Called when display size or font size of settings changed + * + * @param context the current context + */ + public void onDensityOrFontScaleChanged(Context context) { + mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius); + mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius); + } + + /** + * Operates the alpha on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper alpha(SurfaceControl.Transaction tx, SurfaceControl leash, + float alpha) { + tx.setAlpha(leash, alpha); + return this; + } + + /** + * Operates the crop (and position) on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect destinationBounds) { + tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) + .setPosition(leash, destinationBounds.left, destinationBounds.top); + return this; + } + + /** + * Operates the scale (setMatrix) on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, Rect destinationBounds) { + mTmpDestinationRectF.set(destinationBounds); + return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */); + } + + /** + * Operates the scale (setMatrix) on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, RectF destinationBounds) { + return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */); + } + + /** + * Operates the scale (setMatrix) on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, Rect destinationBounds, float degrees) { + mTmpDestinationRectF.set(destinationBounds); + return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees); + } + + /** + * Operates the scale (setMatrix) on a given transaction and leash, along with a rotation. + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, RectF destinationBounds, float degrees) { + mTmpSourceRectF.set(sourceBounds); + // We want the matrix to position the surface relative to the screen coordinates so offset + // the source to 0,0 + mTmpSourceRectF.offsetTo(0, 0); + mTmpDestinationRectF.set(destinationBounds); + mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + mTmpTransform.postRotate(degrees, + mTmpDestinationRectF.centerX(), mTmpDestinationRectF.centerY()); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9); + return this; + } + + /** + * Operates the scale (setMatrix) on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx, + SurfaceControl leash, Rect sourceRectHint, + Rect sourceBounds, Rect destinationBounds, Rect insets, + boolean isInPipDirection, float fraction) { + mTmpDestinationRect.set(sourceBounds); + // Similar to {@link #scale}, we want to position the surface relative to the screen + // coordinates so offset the bounds to 0,0 + mTmpDestinationRect.offsetTo(0, 0); + mTmpDestinationRect.inset(insets); + // Scale to the bounds no smaller than the destination and offset such that the top/left + // of the scaled inset source rect aligns with the top/left of the destination bounds + final float scale; + if (isInPipDirection + && sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) { + // scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only. + final float endScale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceRectHint.width() + : (float) destinationBounds.height() / sourceRectHint.height(); + final float startScale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceBounds.width() + : (float) destinationBounds.height() / sourceBounds.height(); + scale = (1 - fraction) * startScale + fraction * endScale; + } else { + scale = Math.max((float) destinationBounds.width() / sourceBounds.width(), + (float) destinationBounds.height() / sourceBounds.height()); + } + final float left = destinationBounds.left - insets.left * scale; + final float top = destinationBounds.top - insets.top * scale; + mTmpTransform.setScale(scale, scale); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9) + .setCrop(leash, mTmpDestinationRect) + .setPosition(leash, left, top); + return this; + } + + /** + * Operates the rotation according to the given degrees and scale (setMatrix) according to the + * source bounds and rotated destination bounds. The crop will be the unscaled source bounds. + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx, + SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets, + float degrees, float positionX, float positionY, boolean isExpanding, + boolean clockwise) { + mTmpDestinationRect.set(sourceBounds); + mTmpDestinationRect.inset(insets); + final int srcW = mTmpDestinationRect.width(); + final int srcH = mTmpDestinationRect.height(); + final int destW = destinationBounds.width(); + final int destH = destinationBounds.height(); + // Scale by the short side so there won't be empty area if the aspect ratio of source and + // destination are different. + final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH; + final Rect crop = mTmpDestinationRect; + crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH + : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH); + // Inverse scale for crop to fit in screen coordinates. + crop.scale(1 / scale); + crop.offset(insets.left, insets.top); + if (isExpanding) { + // Expand bounds (shrink insets) in source orientation. + positionX -= insets.left * scale; + positionY -= insets.top * scale; + } else { + // Shrink bounds (expand insets) in destination orientation. + if (clockwise) { + positionX -= insets.top * scale; + positionY += insets.left * scale; + } else { + positionX += insets.top * scale; + positionY -= insets.left * scale; + } + } + mTmpTransform.setScale(scale, scale); + mTmpTransform.postRotate(degrees); + mTmpTransform.postTranslate(positionX, positionY); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setCrop(leash, crop); + return this; + } + + /** + * Resets the scale (setMatrix) on a given transaction and leash if there's any + * + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper resetScale(SurfaceControl.Transaction tx, + SurfaceControl leash, + Rect destinationBounds) { + tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9) + .setPosition(leash, destinationBounds.left, destinationBounds.top); + return this; + } + + /** + * Operates the round corner radius on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash, + boolean applyCornerRadius) { + tx.setCornerRadius(leash, applyCornerRadius ? mCornerRadius : 0); + return this; + } + + /** + * Operates the round corner radius on a given transaction and leash, scaled by bounds + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect fromBounds, Rect toBounds) { + final float scale = (float) (Math.hypot(fromBounds.width(), fromBounds.height()) + / Math.hypot(toBounds.width(), toBounds.height())); + tx.setCornerRadius(leash, mCornerRadius * scale); + return this; + } + + /** + * Operates the shadow radius on a given transaction and leash + * @return same {@link PipSurfaceTransactionHelper} instance for method chaining + */ + public PipSurfaceTransactionHelper shadow(SurfaceControl.Transaction tx, SurfaceControl leash, + boolean applyShadowRadius) { + tx.setShadowRadius(leash, applyShadowRadius ? mShadowRadius : 0); + return this; + } + + /** + * Interface to standardize {@link SurfaceControl.Transaction} generation across PiP. + */ + public interface SurfaceControlTransactionFactory { + /** + * @return a new transaction to operate on. + */ + SurfaceControl.Transaction getTransaction(); + } + + /** + * Implementation of {@link SurfaceControlTransactionFactory} that returns + * {@link SurfaceControl.Transaction} with VsyncId being set. + */ + public static class VsyncSurfaceControlTransactionFactory + implements SurfaceControlTransactionFactory { + @Override + public SurfaceControl.Transaction getTransaction() { + final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); + tx.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); + return tx; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java new file mode 100644 index 000000000000..2478252213a7 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java @@ -0,0 +1,608 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip2.phone; + +import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.RemoteAction; +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Debug; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Size; +import android.view.MotionEvent; +import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewRootImpl; +import android.view.WindowManagerGlobal; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipMediaController; +import com.android.wm.shell.common.pip.PipMediaController.ActionListener; +import com.android.wm.shell.common.pip.PipMenuController; +import com.android.wm.shell.common.pip.PipUiEventLogger; +import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; +import com.android.wm.shell.protolog.ShellProtoLogGroup; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * Manages the PiP menu view which can show menu options or a scrim. + * + * The current media session provides actions whenever there are no valid actions provided by the + * current PiP activity. Otherwise, those actions always take precedence. + */ +public class PhonePipMenuController implements PipMenuController { + + private static final String TAG = "PhonePipMenuController"; + private static final boolean DEBUG = false; + + public static final int MENU_STATE_NONE = 0; + public static final int MENU_STATE_FULL = 1; + + /** + * A listener interface to receive notification on changes in PIP. + */ + public interface Listener { + /** + * Called when the PIP menu visibility change has started. + * + * @param menuState the new, about-to-change state of the menu + * @param resize whether or not to resize the PiP with the state change + */ + void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback); + + /** + * Called when the PIP menu state has finished changing/animating. + * + * @param menuState the new state of the menu. + */ + void onPipMenuStateChangeFinish(int menuState); + + /** + * Called when the PIP requested to be expanded. + */ + void onPipExpand(); + + /** + * Called when the PIP requested to be dismissed. + */ + void onPipDismiss(); + + /** + * Called when the PIP requested to show the menu. + */ + void onPipShowMenu(); + + /** + * Called when the PIP requested to enter Split. + */ + void onEnterSplit(); + } + + private final Matrix mMoveTransform = new Matrix(); + private final Rect mTmpSourceBounds = new Rect(); + private final RectF mTmpSourceRectF = new RectF(); + private final RectF mTmpDestinationRectF = new RectF(); + private final Context mContext; + private final PipBoundsState mPipBoundsState; + private final PipMediaController mMediaController; + private final ShellExecutor mMainExecutor; + private final Handler mMainHandler; + + private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory + mSurfaceControlTransactionFactory; + private final float[] mTmpTransform = new float[9]; + + private final ArrayList<Listener> mListeners = new ArrayList<>(); + private final SystemWindows mSystemWindows; + private final PipUiEventLogger mPipUiEventLogger; + + private List<RemoteAction> mAppActions; + private RemoteAction mCloseAction; + private List<RemoteAction> mMediaActions; + + private int mMenuState; + + private PipMenuView mPipMenuView; + + private SurfaceControl mLeash; + + private ActionListener mMediaActionListener = new ActionListener() { + @Override + public void onMediaActionsChanged(List<RemoteAction> mediaActions) { + mMediaActions = new ArrayList<>(mediaActions); + updateMenuActions(); + } + }; + + public PhonePipMenuController(Context context, PipBoundsState pipBoundsState, + PipMediaController mediaController, SystemWindows systemWindows, + PipUiEventLogger pipUiEventLogger, + ShellExecutor mainExecutor, Handler mainHandler) { + mContext = context; + mPipBoundsState = pipBoundsState; + mMediaController = mediaController; + mSystemWindows = systemWindows; + mMainExecutor = mainExecutor; + mMainHandler = mainHandler; + mPipUiEventLogger = pipUiEventLogger; + + mSurfaceControlTransactionFactory = + new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); + } + + public boolean isMenuVisible() { + return mPipMenuView != null && mMenuState != MENU_STATE_NONE; + } + + /** + * Attach the menu when the PiP task first appears. + */ + @Override + public void attach(SurfaceControl leash) { + mLeash = leash; + attachPipMenuView(); + } + + /** + * Detach the menu when the PiP task is gone. + */ + @Override + public void detach() { + hideMenu(); + detachPipMenuView(); + mLeash = null; + } + + void attachPipMenuView() { + // In case detach was not called (e.g. PIP unexpectedly closed) + if (mPipMenuView != null) { + detachPipMenuView(); + } + mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler, + mPipUiEventLogger); + mPipMenuView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + v.getViewRootImpl().addSurfaceChangedCallback( + new ViewRootImpl.SurfaceChangedCallback() { + @Override + public void surfaceCreated(SurfaceControl.Transaction t) { + final SurfaceControl sc = getSurfaceControl(); + if (sc != null) { + t.reparent(sc, mLeash); + // make menu on top of the surface + t.setLayer(sc, Integer.MAX_VALUE); + } + } + + @Override + public void surfaceReplaced(SurfaceControl.Transaction t) { + } + + @Override + public void surfaceDestroyed() { + } + }); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + + mSystemWindows.addView(mPipMenuView, + getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), + 0, SHELL_ROOT_LAYER_PIP); + setShellRootAccessibilityWindow(); + + // Make sure the initial actions are set + updateMenuActions(); + } + + private void detachPipMenuView() { + if (mPipMenuView == null) { + return; + } + + mSystemWindows.removeView(mPipMenuView); + mPipMenuView = null; + } + + /** + * Updates the layout parameters of the menu. + * @param destinationBounds New Menu bounds. + */ + @Override + public void updateMenuBounds(Rect destinationBounds) { + mSystemWindows.updateViewLayout(mPipMenuView, + getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, destinationBounds.width(), + destinationBounds.height())); + updateMenuLayout(destinationBounds); + } + + @Override + public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) { + if (mPipMenuView != null) { + mPipMenuView.onFocusTaskChanged(taskInfo); + } + } + + /** + * Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some + * reason (ie. the window isn't ready yet, thus {@link ViewRootImpl} is + * {@code null}), it will get the leash that the WindowlessWM has assigned to it. + */ + public SurfaceControl getSurfaceControl() { + return mSystemWindows.getViewSurface(mPipMenuView); + } + + /** + * Adds a new menu activity listener. + */ + public void addListener(Listener listener) { + if (!mListeners.contains(listener)) { + mListeners.add(listener); + } + } + + @Nullable + Size getEstimatedMinMenuSize() { + return mPipMenuView == null ? null : mPipMenuView.getEstimatedMinMenuSize(); + } + + /** + * When other components requests the menu controller directly to show the menu, we must + * first fire off the request to the other listeners who will then propagate the call + * back to the controller with the right parameters. + */ + @Override + public void showMenu() { + mListeners.forEach(Listener::onPipShowMenu); + } + + /** + * Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu + * upon PiP window transition is finished. + */ + public void showMenuWithPossibleDelay(int menuState, Rect stackBounds, boolean allowMenuTimeout, + boolean willResizeMenu, boolean showResizeHandle) { + if (willResizeMenu) { + // hide all visible controls including close button and etc. first, this is to ensure + // menu is totally invisible during the transition to eliminate unpleasant artifacts + fadeOutMenu(); + } + showMenuInternal(menuState, stackBounds, allowMenuTimeout, willResizeMenu, + willResizeMenu /* withDelay=willResizeMenu here */, showResizeHandle); + } + + /** + * Shows the menu activity immediately. + */ + public void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout, + boolean willResizeMenu, boolean showResizeHandle) { + showMenuInternal(menuState, stackBounds, allowMenuTimeout, willResizeMenu, + false /* withDelay */, showResizeHandle); + } + + private void showMenuInternal(int menuState, Rect stackBounds, boolean allowMenuTimeout, + boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) { + if (DEBUG) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: showMenu() state=%s" + + " isMenuVisible=%s" + + " allowMenuTimeout=%s" + + " willResizeMenu=%s" + + " withDelay=%s" + + " showResizeHandle=%s" + + " callers=\n%s", TAG, menuState, isMenuVisible(), allowMenuTimeout, + willResizeMenu, withDelay, showResizeHandle, Debug.getCallers(5, " ")); + } + + if (!checkPipMenuState()) { + return; + } + + // Sync the menu bounds before showing it in case it is out of sync. + movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds, + PipMenuController.ALPHA_NO_CHANGE); + updateMenuBounds(stackBounds); + + mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, + showResizeHandle); + } + + /** + * Move the PiP menu, which does a translation and possibly a scale transformation. + */ + @Override + public void movePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds, float alpha) { + if (destinationBounds.isEmpty()) { + return; + } + + if (!checkPipMenuState()) { + return; + } + + // TODO(b/286307861) transaction should be applied outside of PiP menu controller + if (pipLeash != null && t != null) { + t.apply(); + } + } + + /** + * Does an immediate window crop of the PiP menu. + */ + @Override + public void resizePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) { + if (destinationBounds.isEmpty()) { + return; + } + + if (!checkPipMenuState()) { + return; + } + + // TODO(b/286307861) transaction should be applied outside of PiP menu controller + if (pipLeash != null && t != null) { + t.apply(); + } + } + + private boolean checkPipMenuState() { + if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Not going to move PiP, either menu or its parent is not created.", TAG); + return false; + } + + return true; + } + + /** + * Pokes the menu, indicating that the user is interacting with it. + */ + public void pokeMenu() { + final boolean isMenuVisible = isMenuVisible(); + if (DEBUG) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: pokeMenu() isMenuVisible=%b", TAG, isMenuVisible); + } + if (isMenuVisible) { + mPipMenuView.pokeMenu(); + } + } + + private void fadeOutMenu() { + final boolean isMenuVisible = isMenuVisible(); + if (DEBUG) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: fadeOutMenu() isMenuVisible=%b", TAG, isMenuVisible); + } + if (isMenuVisible) { + mPipMenuView.fadeOutMenu(); + } + } + + /** + * Hides the menu view. + */ + public void hideMenu() { + final boolean isMenuVisible = isMenuVisible(); + if (isMenuVisible) { + mPipMenuView.hideMenu(); + } + } + + /** + * Hides the menu view. + * + * @param animationType the animation type to use upon hiding the menu + * @param resize whether or not to resize the PiP with the state change + */ + public void hideMenu(@PipMenuView.AnimationType int animationType, boolean resize) { + final boolean isMenuVisible = isMenuVisible(); + if (DEBUG) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: hideMenu() state=%s" + + " isMenuVisible=%s" + + " animationType=%s" + + " resize=%s" + + " callers=\n%s", TAG, mMenuState, isMenuVisible, + animationType, resize, + Debug.getCallers(5, " ")); + } + if (isMenuVisible) { + mPipMenuView.hideMenu(resize, animationType); + } + } + + /** + * Hides the menu activity. + */ + public void hideMenu(Runnable onStartCallback, Runnable onEndCallback) { + if (isMenuVisible()) { + // If the menu is visible in either the closed or full state, then hide the menu and + // trigger the animation trigger afterwards + if (onStartCallback != null) { + onStartCallback.run(); + } + mPipMenuView.hideMenu(onEndCallback); + } + } + + /** + * Sets the menu actions to the actions provided by the current PiP menu. + */ + @Override + public void setAppActions(List<RemoteAction> appActions, + RemoteAction closeAction) { + mAppActions = appActions; + mCloseAction = closeAction; + updateMenuActions(); + } + + void onPipExpand() { + mListeners.forEach(Listener::onPipExpand); + } + + void onPipDismiss() { + mListeners.forEach(Listener::onPipDismiss); + } + + void onEnterSplit() { + mListeners.forEach(Listener::onEnterSplit); + } + + /** + * @return the best set of actions to show in the PiP menu. + */ + private List<RemoteAction> resolveMenuActions() { + if (isValidActions(mAppActions)) { + return mAppActions; + } + return mMediaActions; + } + + /** + * Updates the PiP menu with the best set of actions provided. + */ + private void updateMenuActions() { + if (mPipMenuView != null) { + mPipMenuView.setActions(mPipBoundsState.getBounds(), + resolveMenuActions(), mCloseAction); + } + } + + /** + * Returns whether the set of actions are valid. + */ + private static boolean isValidActions(List<?> actions) { + return actions != null && actions.size() > 0; + } + + /** + * Handles changes in menu visibility. + */ + void onMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { + if (DEBUG) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: onMenuStateChangeStart() mMenuState=%s" + + " menuState=%s resize=%s" + + " callers=\n%s", TAG, mMenuState, menuState, resize, + Debug.getCallers(5, " ")); + } + + if (menuState != mMenuState) { + mListeners.forEach(l -> l.onPipMenuStateChangeStart(menuState, resize, callback)); + if (menuState == MENU_STATE_FULL) { + // Once visible, start listening for media action changes. This call will trigger + // the menu actions to be updated again. + mMediaController.addActionListener(mMediaActionListener); + } else { + // Once hidden, stop listening for media action changes. This call will trigger + // the menu actions to be updated again. + mMediaController.removeActionListener(mMediaActionListener); + } + + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + mSystemWindows.getFocusGrantToken(mPipMenuView), + menuState != MENU_STATE_NONE /* grantFocus */); + } catch (RemoteException e) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Unable to update focus as menu appears/disappears, %s", TAG, e); + } + } + } + + void onMenuStateChangeFinish(int menuState) { + if (menuState != mMenuState) { + mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState)); + } + mMenuState = menuState; + setShellRootAccessibilityWindow(); + } + + private void setShellRootAccessibilityWindow() { + switch (mMenuState) { + case MENU_STATE_NONE: + mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, null); + break; + default: + mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, + mPipMenuView); + break; + } + } + + /** + * Handles a pointer event sent from pip input consumer. + */ + void handlePointerEvent(MotionEvent ev) { + if (mPipMenuView == null) { + return; + } + + if (ev.isTouchEvent()) { + mPipMenuView.dispatchTouchEvent(ev); + } else { + mPipMenuView.dispatchGenericMotionEvent(ev); + } + } + + /** + * Tell the PIP Menu to recalculate its layout given its current position on the display. + */ + public void updateMenuLayout(Rect bounds) { + final boolean isMenuVisible = isMenuVisible(); + if (DEBUG) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: updateMenuLayout() state=%s" + + " isMenuVisible=%s" + + " callers=\n%s", TAG, mMenuState, isMenuVisible, + Debug.getCallers(5, " ")); + } + if (isMenuVisible) { + mPipMenuView.updateMenuLayout(bounds); + } + } + + void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mMenuState=" + mMenuState); + pw.println(innerPrefix + "mPipMenuView=" + mPipMenuView); + pw.println(innerPrefix + "mListeners=" + mListeners.size()); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuActionView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuActionView.java new file mode 100644 index 000000000000..7252675dc52d --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuActionView.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip2.phone; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.wm.shell.R; + +/** + * Container layout wraps single action image view drawn in PiP menu and can restrict the size of + * action image view (see pip_menu_action.xml). + */ +public class PipMenuActionView extends FrameLayout { + private ImageView mImageView; + private View mCustomCloseBackground; + + public PipMenuActionView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mImageView = findViewById(R.id.image); + mCustomCloseBackground = findViewById(R.id.custom_close_bg); + } + + /** pass through to internal {@link #mImageView} */ + public void setImageDrawable(Drawable drawable) { + mImageView.setImageDrawable(drawable); + } + + /** pass through to internal {@link #mCustomCloseBackground} */ + public void setCustomCloseBackgroundVisibility(@Visibility int visibility) { + mCustomCloseBackground.setVisibility(visibility); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuIconsAlgorithm.java new file mode 100644 index 000000000000..b5e575ba33f2 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuIconsAlgorithm.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip2.phone; + +import android.content.Context; +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +/** + * Helper class to calculate and place the menu icons on the PIP Menu. + */ +public class PipMenuIconsAlgorithm { + + private static final String TAG = "PipMenuIconsAlgorithm"; + + protected ViewGroup mViewRoot; + protected ViewGroup mTopEndContainer; + protected View mDragHandle; + protected View mEnterSplitButton; + protected View mSettingsButton; + protected View mDismissButton; + + protected PipMenuIconsAlgorithm(Context context) { + } + + /** + * Bind the necessary views. + */ + public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle, + View enterSplitButton, View settingsButton, View dismissButton) { + mViewRoot = viewRoot; + mTopEndContainer = topEndContainer; + mDragHandle = dragHandle; + mEnterSplitButton = enterSplitButton; + mSettingsButton = settingsButton; + mDismissButton = dismissButton; + } + + /** + * Updates the position of the drag handle based on where the PIP window is on the screen. + */ + public void onBoundsChanged(Rect bounds) { + // On phones, the menu icons are always static and will never move based on the PIP window + // position. No need to do anything here. + } + + /** + * Set the gravity on the given view. + */ + protected static void setLayoutGravity(View v, int gravity) { + if (v.getLayoutParams() instanceof FrameLayout.LayoutParams) { + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v.getLayoutParams(); + params.gravity = gravity; + v.setLayoutParams(params); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java new file mode 100644 index 000000000000..a5b76c7df20b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java @@ -0,0 +1,630 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip2.phone; + +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS; +import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS; +import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; + +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.PendingIntent; +import android.app.RemoteAction; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.util.Pair; +import android.util.Size; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.R; +import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.pip.PipUiEventLogger; +import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.protolog.ShellProtoLogGroup; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Translucent window that gets started on top of a task in PIP to allow the user to control it. + */ +public class PipMenuView extends FrameLayout { + + private static final String TAG = "PipMenuView"; + + private static final int ANIMATION_NONE_DURATION_MS = 0; + private static final int ANIMATION_HIDE_DURATION_MS = 125; + + /** No animation performed during menu hide. */ + public static final int ANIM_TYPE_NONE = 0; + /** Fade out the menu until it's invisible. Used when the PIP window remains visible. */ + public static final int ANIM_TYPE_HIDE = 1; + /** Fade out the menu in sync with the PIP window. */ + public static final int ANIM_TYPE_DISMISS = 2; + + @IntDef(prefix = { "ANIM_TYPE_" }, value = { + ANIM_TYPE_NONE, + ANIM_TYPE_HIDE, + ANIM_TYPE_DISMISS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AnimationType {} + + private static final int INITIAL_DISMISS_DELAY = 3500; + private static final int POST_INTERACTION_DISMISS_DELAY = 2000; + private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30; + + private static final float MENU_BACKGROUND_ALPHA = 0.54f; + private static final float DISABLED_ACTION_ALPHA = 0.54f; + + private int mMenuState; + private boolean mAllowMenuTimeout = true; + private boolean mAllowTouches = true; + private int mDismissFadeOutDurationMs; + private final List<RemoteAction> mActions = new ArrayList<>(); + private RemoteAction mCloseAction; + + private AccessibilityManager mAccessibilityManager; + private Drawable mBackgroundDrawable; + private View mMenuContainer; + private LinearLayout mActionsGroup; + private int mBetweenActionPaddingLand; + + private AnimatorSet mMenuContainerAnimator; + private final PhonePipMenuController mController; + private final PipUiEventLogger mPipUiEventLogger; + + private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener = + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final float alpha = (float) animation.getAnimatedValue(); + mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA * alpha * 255)); + } + }; + + private ShellExecutor mMainExecutor; + private Handler mMainHandler; + + /** + * Whether the most recent showing of the menu caused a PIP resize, such as when PIP is too + * small and it is resized on menu show to fit the actions. + */ + private boolean mDidLastShowMenuResize; + private final Runnable mHideMenuRunnable = this::hideMenu; + + protected View mViewRoot; + protected View mSettingsButton; + protected View mDismissButton; + protected View mEnterSplitButton; + protected View mTopEndContainer; + protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; + + // How long the shell will wait for the app to close the PiP if a custom action is set. + private final int mPipForceCloseDelay; + + public PipMenuView(Context context, PhonePipMenuController controller, + ShellExecutor mainExecutor, Handler mainHandler, PipUiEventLogger pipUiEventLogger) { + super(context, null, 0); + mContext = context; + mController = controller; + mMainExecutor = mainExecutor; + mMainHandler = mainHandler; + mPipUiEventLogger = pipUiEventLogger; + + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + inflate(context, R.layout.pip_menu, this); + + mPipForceCloseDelay = context.getResources().getInteger( + R.integer.config_pipForceCloseDelay); + + mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background); + mBackgroundDrawable.setAlpha(0); + mViewRoot = findViewById(R.id.background); + mViewRoot.setBackground(mBackgroundDrawable); + mMenuContainer = findViewById(R.id.menu_container); + mMenuContainer.setAlpha(0); + mTopEndContainer = findViewById(R.id.top_end_container); + mSettingsButton = findViewById(R.id.settings); + mSettingsButton.setAlpha(0); + mSettingsButton.setOnClickListener((v) -> { + if (v.getAlpha() != 0) { + showSettings(); + } + }); + mDismissButton = findViewById(R.id.dismiss); + mDismissButton.setAlpha(0); + mDismissButton.setOnClickListener(v -> dismissPip()); + findViewById(R.id.expand_button).setOnClickListener(v -> { + if (mMenuContainer.getAlpha() != 0) { + expandPip(); + } + }); + + mEnterSplitButton = findViewById(R.id.enter_split); + mEnterSplitButton.setAlpha(0); + mEnterSplitButton.setOnClickListener(v -> { + if (mEnterSplitButton.getAlpha() != 0) { + enterSplit(); + } + }); + + // this disables the ripples + mEnterSplitButton.setEnabled(false); + + findViewById(R.id.resize_handle).setAlpha(0); + + mActionsGroup = findViewById(R.id.actions_group); + mBetweenActionPaddingLand = getResources().getDimensionPixelSize( + R.dimen.pip_between_action_padding_land); + mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext); + mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer, + findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton, + mDismissButton); + mDismissFadeOutDurationMs = context.getResources() + .getInteger(R.integer.config_pipExitAnimationDuration); + + initAccessibility(); + } + + private void initAccessibility() { + this.setAccessibilityDelegate(new AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + String label = getResources().getString(R.string.pip_menu_title); + info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label)); + } + + @Override + public boolean performAccessibilityAction(View host, int action, Bundle args) { + if (action == ACTION_CLICK && mMenuState != MENU_STATE_FULL) { + mController.showMenu(); + } + return super.performAccessibilityAction(host, action, args); + } + }); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_ESCAPE) { + hideMenu(); + return true; + } + return super.onKeyUp(keyCode, event); + } + + @Override + public boolean shouldDelayChildPressedState() { + return true; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (!mAllowTouches) { + return false; + } + + if (mAllowMenuTimeout) { + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); + } + + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + if (mAllowMenuTimeout) { + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); + } + + return super.dispatchGenericMotionEvent(event); + } + + void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {} + + void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout, + boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) { + mAllowMenuTimeout = allowMenuTimeout; + mDidLastShowMenuResize = resizeMenuOnShow; + final boolean enableEnterSplit = + mContext.getResources().getBoolean(R.bool.config_pipEnableEnterSplitButton); + if (mMenuState != menuState) { + // Disallow touches if the menu needs to resize while showing, and we are transitioning + // to/from a full menu state. + boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow + && (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL); + mAllowTouches = !disallowTouchesUntilAnimationEnd; + cancelDelayedHide(); + if (mMenuContainerAnimator != null) { + mMenuContainerAnimator.cancel(); + } + mMenuContainerAnimator = new AnimatorSet(); + ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, + mMenuContainer.getAlpha(), 1f); + menuAnim.addUpdateListener(mMenuBgUpdateListener); + ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, + mSettingsButton.getAlpha(), 1f); + ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA, + mDismissButton.getAlpha(), 1f); + if (menuState == MENU_STATE_FULL) { + mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim); + } + mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN); + mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS); + mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAllowTouches = true; + notifyMenuStateChangeFinish(menuState); + if (allowMenuTimeout) { + repostDelayedHide(INITIAL_DISMISS_DELAY); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mAllowTouches = true; + } + }); + if (withDelay) { + // starts the menu container animation after window expansion is completed + notifyMenuStateChangeStart(menuState, resizeMenuOnShow, () -> { + if (mMenuContainerAnimator == null) { + return; + } + mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY); + setVisibility(VISIBLE); + mMenuContainerAnimator.start(); + }); + } else { + notifyMenuStateChangeStart(menuState, resizeMenuOnShow, null); + setVisibility(VISIBLE); + mMenuContainerAnimator.start(); + } + updateActionViews(menuState, stackBounds); + } else { + // If we are already visible, then just start the delayed dismiss and unregister any + // existing input consumers from the previous drag + if (allowMenuTimeout) { + repostDelayedHide(POST_INTERACTION_DISMISS_DELAY); + } + } + } + + /** + * Different from {@link #hideMenu()}, this function does not try to finish this menu activity + * and instead, it fades out the controls by setting the alpha to 0 directly without menu + * visibility callbacks invoked. + */ + void fadeOutMenu() { + mMenuContainer.setAlpha(0f); + mSettingsButton.setAlpha(0f); + mDismissButton.setAlpha(0f); + mEnterSplitButton.setAlpha(0f); + } + + void pokeMenu() { + cancelDelayedHide(); + } + + void updateMenuLayout(Rect bounds) { + mPipMenuIconsAlgorithm.onBoundsChanged(bounds); + } + + void hideMenu() { + hideMenu(null); + } + + void hideMenu(Runnable animationEndCallback) { + hideMenu(animationEndCallback, true /* notifyMenuVisibility */, mDidLastShowMenuResize, + ANIM_TYPE_HIDE); + } + + void hideMenu(boolean resize, @AnimationType int animationType) { + hideMenu(null /* animationFinishedRunnable */, true /* notifyMenuVisibility */, resize, + animationType); + } + + void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility, + boolean resize, @AnimationType int animationType) { + if (mMenuState != MENU_STATE_NONE) { + cancelDelayedHide(); + if (notifyMenuVisibility) { + notifyMenuStateChangeStart(MENU_STATE_NONE, resize, null); + } + mMenuContainerAnimator = new AnimatorSet(); + ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, + mMenuContainer.getAlpha(), 0f); + menuAnim.addUpdateListener(mMenuBgUpdateListener); + ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, + mSettingsButton.getAlpha(), 0f); + ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA, + mDismissButton.getAlpha(), 0f); + ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA, + mEnterSplitButton.getAlpha(), 0f); + mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, + enterSplitAnim); + mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT); + mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType)); + mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setVisibility(GONE); + if (notifyMenuVisibility) { + notifyMenuStateChangeFinish(MENU_STATE_NONE); + } + if (animationFinishedRunnable != null) { + animationFinishedRunnable.run(); + } + } + }); + mMenuContainerAnimator.start(); + } + } + + /** + * @return Estimated minimum {@link Size} to hold the actions. + * See also {@link #updateActionViews(Rect)} + */ + Size getEstimatedMinMenuSize() { + final int pipActionSize = getResources().getDimensionPixelSize(R.dimen.pip_action_size); + // the minimum width would be (2 * pipActionSize) since we have settings and dismiss button + // on the top action container. + final int width = Math.max(2, mActions.size()) * pipActionSize; + final int height = getResources().getDimensionPixelSize(R.dimen.pip_expand_action_size) + + getResources().getDimensionPixelSize(R.dimen.pip_action_padding) + + getResources().getDimensionPixelSize(R.dimen.pip_expand_container_edge_margin); + return new Size(width, height); + } + + void setActions(Rect stackBounds, @Nullable List<RemoteAction> actions, + @Nullable RemoteAction closeAction) { + mActions.clear(); + if (actions != null && !actions.isEmpty()) { + mActions.addAll(actions); + } + mCloseAction = closeAction; + if (mMenuState == MENU_STATE_FULL) { + updateActionViews(mMenuState, stackBounds); + } + } + + private void updateActionViews(int menuState, Rect stackBounds) { + ViewGroup expandContainer = findViewById(R.id.expand_container); + ViewGroup actionsContainer = findViewById(R.id.actions_container); + actionsContainer.setOnTouchListener((v, ev) -> { + // Do nothing, prevent click through to parent + return true; + }); + + // Update the expand button only if it should show with the menu + expandContainer.setVisibility(menuState == MENU_STATE_FULL + ? View.VISIBLE + : View.INVISIBLE); + + LayoutParams expandedLp = + (LayoutParams) expandContainer.getLayoutParams(); + if (mActions.isEmpty() || menuState == MENU_STATE_NONE) { + actionsContainer.setVisibility(View.INVISIBLE); + + // Update the expand container margin to adjust the center of the expand button to + // account for the existence of the action container + expandedLp.topMargin = 0; + expandedLp.bottomMargin = 0; + } else { + actionsContainer.setVisibility(View.VISIBLE); + if (mActionsGroup != null) { + // Ensure we have as many buttons as actions + final LayoutInflater inflater = LayoutInflater.from(mContext); + while (mActionsGroup.getChildCount() < mActions.size()) { + final PipMenuActionView actionView = (PipMenuActionView) inflater.inflate( + R.layout.pip_menu_action, mActionsGroup, false); + mActionsGroup.addView(actionView); + } + + // Update the visibility of all views + for (int i = 0; i < mActionsGroup.getChildCount(); i++) { + mActionsGroup.getChildAt(i).setVisibility(i < mActions.size() + ? View.VISIBLE + : View.GONE); + } + + // Recreate the layout + final boolean isLandscapePip = stackBounds != null + && (stackBounds.width() > stackBounds.height()); + for (int i = 0; i < mActions.size(); i++) { + final RemoteAction action = mActions.get(i); + final PipMenuActionView actionView = + (PipMenuActionView) mActionsGroup.getChildAt(i); + final boolean isCloseAction = mCloseAction != null && Objects.equals( + mCloseAction.getActionIntent(), action.getActionIntent()); + + final int iconType = action.getIcon().getType(); + if (iconType == Icon.TYPE_URI || iconType == Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // Disallow loading icon from content URI + actionView.setImageDrawable(null); + } else { + // TODO: Check if the action drawable has changed before we reload it + action.getIcon().loadDrawableAsync(mContext, d -> { + if (d != null) { + d.setTint(Color.WHITE); + actionView.setImageDrawable(d); + } + }, mMainHandler); + } + actionView.setCustomCloseBackgroundVisibility( + isCloseAction ? View.VISIBLE : View.GONE); + actionView.setContentDescription(action.getContentDescription()); + if (action.isEnabled()) { + actionView.setOnClickListener( + v -> onActionViewClicked(action.getActionIntent(), isCloseAction)); + } + actionView.setEnabled(action.isEnabled()); + actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); + + // Update the margin between actions + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) + actionView.getLayoutParams(); + lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0; + } + } + + // Update the expand container margin to adjust the center of the expand button to + // account for the existence of the action container + expandedLp.topMargin = getResources().getDimensionPixelSize( + R.dimen.pip_action_padding); + expandedLp.bottomMargin = getResources().getDimensionPixelSize( + R.dimen.pip_expand_container_edge_margin); + } + expandContainer.requestLayout(); + } + + private void notifyMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { + mController.onMenuStateChangeStart(menuState, resize, callback); + } + + private void notifyMenuStateChangeFinish(int menuState) { + mMenuState = menuState; + mController.onMenuStateChangeFinish(menuState); + } + + private void expandPip() { + // Do not notify menu visibility when hiding the menu, the controller will do this when it + // handles the message + hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* resize */, + ANIM_TYPE_HIDE); + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); + } + + private void dismissPip() { + if (mMenuState != MENU_STATE_NONE) { + // Do not call hideMenu() directly. Instead, let the menu controller handle it just as + // any other dismissal that will update the touch state and fade out the PIP task + // and the menu view at the same time. + mController.onPipDismiss(); + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE); + } + } + + /** + * Execute the {@link PendingIntent} attached to the {@link PipMenuActionView}. + * If the given {@link PendingIntent} matches {@link #mCloseAction}, we need to make sure + * the PiP is removed after a certain timeout in case the app does not respond in a + * timely manner. + */ + private void onActionViewClicked(@NonNull PendingIntent intent, boolean isCloseAction) { + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: Failed to send action, %s", TAG, e); + } + if (isCloseAction) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_CUSTOM_CLOSE); + mAllowTouches = false; + mMainExecutor.executeDelayed(() -> { + hideMenu(); + // TODO: it's unsafe to call onPipDismiss with a delay here since + // we may have a different PiP by the time this runnable is executed. + mController.onPipDismiss(); + mAllowTouches = true; + }, mPipForceCloseDelay); + } + } + + private void enterSplit() { + // Do not notify menu visibility when hiding the menu, the controller will do this when it + // handles the message + hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */, + ANIM_TYPE_HIDE); + } + + + private void showSettings() { + final Pair<ComponentName, Integer> topPipActivityInfo = + PipUtils.getTopPipActivity(mContext); + if (topPipActivityInfo.first != null) { + final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, + Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null)); + settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second)); + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_SETTINGS); + } + } + + private void cancelDelayedHide() { + mMainExecutor.removeCallbacks(mHideMenuRunnable); + } + + private void repostDelayedHide(int delay) { + int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay, + FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS); + mMainExecutor.removeCallbacks(mHideMenuRunnable); + mMainExecutor.executeDelayed(mHideMenuRunnable, recommendedTimeout); + } + + private long getFadeOutDuration(@AnimationType int animationType) { + switch (animationType) { + case ANIM_TYPE_NONE: + return ANIMATION_NONE_DURATION_MS; + case ANIM_TYPE_HIDE: + return ANIMATION_HIDE_DURATION_MS; + case ANIM_TYPE_DISMISS: + return mDismissFadeOutDurationMs; + default: + throw new IllegalStateException("Invalid animation type " + animationType); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index f3d178aef4ea..fbf4d13a0c19 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -42,8 +42,8 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUtils; -import com.android.wm.shell.pip.PipMenuController; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; 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 e6faa6391cca..96eaa1edbae4 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 @@ -287,7 +287,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL } boolean isHandlingDragResize() { - return mDragResizeListener.isHandlingDragResize(); + return mDragResizeListener != null && mDragResizeListener.isHandlingDragResize(); } private void closeDragResizeListener() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 4ba05ce8aef1..a8b39c41e6bb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -345,7 +345,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTaskOperations.injectBackKey(); } else if (id == R.id.caption_handle || id == R.id.open_menu_button) { if (!decoration.isHandleMenuActive()) { - moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId)); + moveTaskToFront(decoration.mTaskInfo); decoration.createHandleMenu(); } else { decoration.closeHandleMenu(); @@ -419,10 +419,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && id != R.id.maximize_window) { return false; } - moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId)); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + moveTaskToFront(decoration.mTaskInfo); if (!mHasLongClicked && id != R.id.maximize_window) { - final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); decoration.closeMaximizeMenuIfNeeded(e); } @@ -466,7 +466,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { */ @Override public boolean handleMotionEvent(@Nullable View v, MotionEvent e) { - final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + final RunningTaskInfo taskInfo = decoration.mTaskInfo; if (DesktopModeStatus.isEnabled() && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { return false; @@ -492,8 +493,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } case MotionEvent.ACTION_MOVE: { mShouldClick = false; - final DesktopModeWindowDecoration decoration = - mWindowDecorByTaskId.get(mTaskId); // If a decor's resize drag zone is active, don't also try to reposition it. if (decoration.isHandlingDragResize()) break; decoration.closeMaximizeMenu(); @@ -557,9 +556,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && action != MotionEvent.ACTION_CANCEL)) { return false; } - final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); - mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo, - mWindowDecorByTaskId.get(taskInfo.taskId))); + mDesktopTasksController.ifPresent(c -> { + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + c.toggleDesktopTaskSize(decoration.mTaskInfo, decoration); + }); return true; } } @@ -843,7 +843,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Nullable private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) { - if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) { + final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); + if (focusedDecor == null) { + return null; + } + final boolean splitScreenVisible = mSplitScreenController != null + && mSplitScreenController.isSplitScreenVisible(); + // It's possible that split tasks are visible but neither is focused, such as when there's + // a fullscreen translucent window on top of them. In that case, the relevant decor should + // just be that translucent focused window. + final boolean focusedTaskInSplit = mSplitScreenController != null + && mSplitScreenController.isTaskInSplitScreen(focusedDecor.mTaskInfo.taskId); + if (splitScreenVisible && focusedTaskInSplit) { // We can't look at focused task here as only one task will have focus. DesktopModeWindowDecoration splitTaskDecor = getSplitScreenDecor(ev); return splitTaskDecor == null ? getFocusedDecor() : splitTaskDecor; 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 20233331997f..3f0a28118597 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 @@ -36,7 +36,6 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; import android.os.Handler; -import android.util.Log; import android.view.Choreographer; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -388,27 +387,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } boolean isHandlingDragResize() { - return mDragResizeListener.isHandlingDragResize(); + return mDragResizeListener != null && mDragResizeListener.isHandlingDragResize(); } private void loadAppInfo() { - String packageName = mTaskInfo.realActivity.getPackageName(); PackageManager pm = mContext.getApplicationContext().getPackageManager(); - try { - final IconProvider provider = new IconProvider(mContext); - mAppIconDrawable = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity, - PackageManager.ComponentInfoFlags.of(0))); - final Resources resources = mContext.getResources(); - final BaseIconFactory factory = new BaseIconFactory(mContext, - resources.getDisplayMetrics().densityDpi, - resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius)); - mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT); - final ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, - PackageManager.ApplicationInfoFlags.of(0)); - mAppName = pm.getApplicationLabel(applicationInfo); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Package not found: " + packageName, e); - } + final IconProvider provider = new IconProvider(mContext); + mAppIconDrawable = provider.getIcon(mTaskInfo.topActivityInfo); + final Resources resources = mContext.getResources(); + final BaseIconFactory factory = new BaseIconFactory(mContext, + resources.getDisplayMetrics().densityDpi, + resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius)); + mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT); + final ApplicationInfo applicationInfo = mTaskInfo.topActivityInfo.applicationInfo; + mAppName = pm.getApplicationLabel(applicationInfo); } private void closeDragResizeListener() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 8b38f991a2db..d902621180d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -353,6 +353,7 @@ class DragResizeInputListener implements AutoCloseable { private boolean mShouldHandleEvents; private int mLastCursorType = PointerIcon.TYPE_DEFAULT; private Rect mDragStartTaskBounds; + private final Rect mTmpRect = new Rect(); private TaskResizeInputEventReceiver( InputChannel inputChannel, Handler handler, Choreographer choreographer) { @@ -477,14 +478,15 @@ class DragResizeInputListener implements AutoCloseable { } private void updateInputSinkRegionForDrag(Rect taskBounds) { + mTmpRect.set(taskBounds); final DisplayLayout layout = mDisplayController.getDisplayLayout(mDisplayId); final Region dragTouchRegion = new Region(-taskBounds.left, -taskBounds.top, -taskBounds.left + layout.width(), -taskBounds.top + layout.height()); // Remove the localized task bounds from the touch region. - taskBounds.offsetTo(0, 0); - dragTouchRegion.op(taskBounds, Region.Op.DIFFERENCE); + mTmpRect.offsetTo(0, 0); + dragTouchRegion.op(mTmpRect, Region.Op.DIFFERENCE); updateSinkInputChannel(dragTouchRegion); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java index 368231e2d7f0..b0d3b5090ef0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java @@ -125,6 +125,7 @@ public class ResizeVeil { relayout(taskBounds, t); if (fadeIn) { + cancelAnimation(); mVeilAnimator = new ValueAnimator(); mVeilAnimator.setFloatValues(0f, 1f); mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION); @@ -210,15 +211,16 @@ public class ResizeVeil { * Animate veil's alpha to 0, fading it out. */ public void hideVeil() { - final ValueAnimator animator = new ValueAnimator(); - animator.setFloatValues(1, 0); - animator.setDuration(RESIZE_ALPHA_DURATION); - animator.addUpdateListener(animation -> { + cancelAnimation(); + mVeilAnimator = new ValueAnimator(); + mVeilAnimator.setFloatValues(1, 0); + mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION); + mVeilAnimator.addUpdateListener(animation -> { SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - t.setAlpha(mVeilSurface, 1 - animator.getAnimatedFraction()); + t.setAlpha(mVeilSurface, 1 - mVeilAnimator.getAnimatedFraction()); t.apply(); }); - animator.addListener(new AnimatorListenerAdapter() { + mVeilAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); @@ -226,7 +228,7 @@ public class ResizeVeil { t.apply(); } }); - animator.start(); + mVeilAnimator.start(); } @ColorRes @@ -240,10 +242,20 @@ public class ResizeVeil { } } + private void cancelAnimation() { + if (mVeilAnimator != null) { + mVeilAnimator.removeAllUpdateListeners(); + mVeilAnimator.cancel(); + } + } + /** * Dispose of veil when it is no longer needed, likely on close of its container decor. */ void dispose() { + cancelAnimation(); + mVeilAnimator = null; + if (mViewHost != null) { mViewHost.release(); mViewHost = null; diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp index e61f7629f4fd..faeb342a44be 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp @@ -30,6 +30,7 @@ filegroup { "src/**/B*.kt", "src/**/C*.kt", "src/**/D*.kt", + "src/**/F*.kt", "src/**/S*.kt", ], } diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt index a5c2c8988e70..27922988038c 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit -import android.tools.common.NavBar import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder @@ -62,7 +61,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : +class FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : AutoEnterPipOnGoToHomeTest(flicker) { private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) /** Second app used to enter split screen mode */ @@ -120,9 +119,7 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : @Presubmit @Test override fun pipAppLayerAlwaysVisible() { - // pip layer in gesture nav will disappear during transition with alpha animation - Assume.assumeFalse(flicker.scenario.isGesturalNavigation) - super.pipAppLayerAlwaysVisible() + // pip layer in should disappear during transition with alpha animation } @Presubmit @@ -151,8 +148,7 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : @JvmStatic fun getParams() = LegacyFlickerTestFactory.nonRotationTests( - // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt new file mode 100644 index 000000000000..4c2315324eca --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +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.helpers.WindowUtils +import android.tools.device.traces.parsers.toFlickerComponent +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.wm.shell.flicker.pip.common.EnterPipTransition +import com.android.wm.shell.flicker.utils.SplitScreenUtils +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from an app via auto-enter property when navigating to home from split screen. + * + * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest` + * + * Actions: + * ``` + * Launch an app in full screen + * Select "Auto-enter PiP" radio button + * Open all apps and drag another app icon to enter split screen + * Press Home button or swipe up to go Home and put [pipApp] in pip mode + * ``` + * + * Notes: + * ``` + * 1. All assertions are inherited from [EnterPipTest] + * 2. Part of the test setup occurs automatically via + * [android.tools.device.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : + EnterPipTransition(flicker) { + private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) + /** Second app used to enter split screen mode */ + private val secondAppForSplitScreen = + SimpleAppHelper( + instrumentation, + ActivityOptions.SplitScreen.Primary.LABEL, + ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent() + ) + + /** Defines the transition used to run the test */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + secondAppForSplitScreen.launchViaIntent(wmHelper) + pipApp.launchViaIntent(wmHelper) + tapl.goHome() + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + pipApp, + secondAppForSplitScreen, + flicker.scenario.startRotation + ) + pipApp.enableEnterPipOnUserLeaveHint() + } + teardown { + pipApp.exit(wmHelper) + secondAppForSplitScreen.exit(wmHelper) + } + transitions { tapl.goHome() } + } + + @Presubmit + @Test + override fun pipOverlayLayerAppearThenDisappear() { + // when entering from split screen we use alpha animation, without overlay + } + + @Presubmit + @Test + override fun pipLayerOrOverlayRemainInsideVisibleBounds() { + // when entering from split screen we use alpha animation, without overlay + } + + @Presubmit + @Test + override fun pipLayerReduces() { + // when entering from split screen we use alpha animation, so there is no size change + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipLayerReduces() + } + + @Presubmit + @Test + override fun pipAppLayerAlwaysVisible() { + // pip layer in should disappear during transition with alpha animation + } + + @Presubmit + @Test + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.focusChanges() + } + + @Presubmit + @Test + fun pipAppWindowVisibleChanges() { + // TODO(b/322394235) this method comes from EnterPipOnUserLeaveHintTest, but due to how + // it is being packaged in Android.bp we cannot inherit from it. Needs to be refactored. + Assume.assumeTrue(flicker.scenario.isGesturalNavigation) + flicker.assertWm { + this.isAppWindowVisible(pipApp) + .then() + .isAppWindowInvisible(pipApp, isOptional = true) + .then() + .isAppWindowVisible(pipApp, isOptional = true) + } + } + + @Presubmit + @Test + override fun pipAppWindowAlwaysVisible() { + // TODO(b/322394235) this method comes from EnterPipOnUserLeaveHintTest, but due to how + // it is being packaged in Android.bp we cannot inherit from it. Needs to be refactored. + // In gestural nav the pip will first move behind home and then above home. The visual + // appearance visible->invisible->visible is asserted by pipAppLayerAlwaysVisible(). + // But the internal states of activity don't need to follow that, such as a temporary + // visibility state can be changed quickly outside a transaction so the test doesn't + // detect that. Hence, skip the case to avoid restricting the internal implementation. + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipAppWindowAlwaysVisible() + } + + @Presubmit + @Test + override fun pipWindowRemainInsideVisibleBounds() { + if (tapl.isTablet) { + flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } + } else { + // on phones home screen does not rotate in landscape, PiP enters back to portrait + // orientation - if we go from landscape to portrait it should switch between the bounds + // otherwise it should be the same as tablet (i.e. portrait to portrait) + if (flicker.scenario.isLandscapeOrSeascapeAtStart) { + flicker.assertWmVisibleRegion(pipApp) { + // first check against landscape bounds then against portrait bounds + coversAtMost(displayBounds).then().coversAtMost(portraitDisplayBounds) + } + } else { + // always check against the display bounds which do not change during transition + flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} 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 193f16da3e39..40e61dd95f51 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 @@ -27,6 +27,8 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -202,6 +204,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { .setTaskDescriptionBuilder(taskDescriptionBuilder) .setVisible(visible) .build(); + taskInfo.topActivityInfo = new ActivityInfo(); + taskInfo.topActivityInfo.applicationInfo = new ApplicationInfo(); taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor", "DesktopModeWindowDecorationTests"); taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor", diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 0abb6f5ed011..4e330da417be 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -629,14 +629,6 @@ cc_defaults { // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed. cflags: ["-Wno-implicit-fallthrough"], }, - host: { - srcs: [ - "utils/HostColorSpace.cpp", - ], - export_static_lib_headers: [ - "libarect", - ], - }, }, } diff --git a/libs/hwui/utils/HostColorSpace.cpp b/libs/hwui/utils/HostColorSpace.cpp deleted file mode 100644 index 77a6820c6999..000000000000 --- a/libs/hwui/utils/HostColorSpace.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// This is copied from framework/native/libs/ui in order not to include libui in host build - -#include <ui/ColorSpace.h> - -using namespace std::placeholders; - -namespace android { - -static constexpr float linearResponse(float v) { - return v; -} - -static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) { - return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c; -} - -static constexpr float response(float x, const ColorSpace::TransferParameters& p) { - return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x; -} - -static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) { - return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c; -} - -static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) { - return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f; -} - -static float absRcpResponse(float x, float g,float a, float b, float c, float d) { - float xx = std::abs(x); - return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x); -} - -static float absResponse(float x, float g, float a, float b, float c, float d) { - float xx = std::abs(x); - return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x); -} - -static float safePow(float x, float e) { - return powf(x < 0.0f ? 0.0f : x, e); -} - -static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) { - if (parameters.e == 0.0f && parameters.f == 0.0f) { - return std::bind(rcpResponse, _1, parameters); - } - return std::bind(rcpFullResponse, _1, parameters); -} - -static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) { - if (parameters.e == 0.0f && parameters.f == 0.0f) { - return std::bind(response, _1, parameters); - } - return std::bind(fullResponse, _1, parameters); -} - -static ColorSpace::transfer_function toOETF(float gamma) { - if (gamma == 1.0f) { - return linearResponse; - } - return std::bind(safePow, _1, 1.0f / gamma); -} - -static ColorSpace::transfer_function toEOTF(float gamma) { - if (gamma == 1.0f) { - return linearResponse; - } - return std::bind(safePow, _1, gamma); -} - -static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) { - float3 r(rgbToXYZ * float3{1, 0, 0}); - float3 g(rgbToXYZ * float3{0, 1, 0}); - float3 b(rgbToXYZ * float3{0, 0, 1}); - - return {{r.xy / dot(r, float3{1}), - g.xy / dot(g, float3{1}), - b.xy / dot(b, float3{1})}}; -} - -static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) { - float3 w(rgbToXYZ * float3{1}); - return w.xy / dot(w, float3{1}); -} - -ColorSpace::ColorSpace( - const std::string& name, - const mat3& rgbToXYZ, - transfer_function OETF, - transfer_function EOTF, - clamping_function clamper) noexcept - : mName(name) - , mRGBtoXYZ(rgbToXYZ) - , mXYZtoRGB(inverse(rgbToXYZ)) - , mOETF(std::move(OETF)) - , mEOTF(std::move(EOTF)) - , mClamper(std::move(clamper)) - , mPrimaries(computePrimaries(rgbToXYZ)) - , mWhitePoint(computeWhitePoint(rgbToXYZ)) { -} - -ColorSpace::ColorSpace( - const std::string& name, - const mat3& rgbToXYZ, - const TransferParameters parameters, - clamping_function clamper) noexcept - : mName(name) - , mRGBtoXYZ(rgbToXYZ) - , mXYZtoRGB(inverse(rgbToXYZ)) - , mParameters(parameters) - , mOETF(toOETF(mParameters)) - , mEOTF(toEOTF(mParameters)) - , mClamper(std::move(clamper)) - , mPrimaries(computePrimaries(rgbToXYZ)) - , mWhitePoint(computeWhitePoint(rgbToXYZ)) { -} - -ColorSpace::ColorSpace( - const std::string& name, - const mat3& rgbToXYZ, - float gamma, - clamping_function clamper) noexcept - : mName(name) - , mRGBtoXYZ(rgbToXYZ) - , mXYZtoRGB(inverse(rgbToXYZ)) - , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) - , mOETF(toOETF(gamma)) - , mEOTF(toEOTF(gamma)) - , mClamper(std::move(clamper)) - , mPrimaries(computePrimaries(rgbToXYZ)) - , mWhitePoint(computeWhitePoint(rgbToXYZ)) { -} - -ColorSpace::ColorSpace( - const std::string& name, - const std::array<float2, 3>& primaries, - const float2& whitePoint, - transfer_function OETF, - transfer_function EOTF, - clamping_function clamper) noexcept - : mName(name) - , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) - , mXYZtoRGB(inverse(mRGBtoXYZ)) - , mOETF(std::move(OETF)) - , mEOTF(std::move(EOTF)) - , mClamper(std::move(clamper)) - , mPrimaries(primaries) - , mWhitePoint(whitePoint) { -} - -ColorSpace::ColorSpace( - const std::string& name, - const std::array<float2, 3>& primaries, - const float2& whitePoint, - const TransferParameters parameters, - clamping_function clamper) noexcept - : mName(name) - , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) - , mXYZtoRGB(inverse(mRGBtoXYZ)) - , mParameters(parameters) - , mOETF(toOETF(mParameters)) - , mEOTF(toEOTF(mParameters)) - , mClamper(std::move(clamper)) - , mPrimaries(primaries) - , mWhitePoint(whitePoint) { -} - -ColorSpace::ColorSpace( - const std::string& name, - const std::array<float2, 3>& primaries, - const float2& whitePoint, - float gamma, - clamping_function clamper) noexcept - : mName(name) - , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) - , mXYZtoRGB(inverse(mRGBtoXYZ)) - , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) - , mOETF(toOETF(gamma)) - , mEOTF(toEOTF(gamma)) - , mClamper(std::move(clamper)) - , mPrimaries(primaries) - , mWhitePoint(whitePoint) { -} - -constexpr mat3 ColorSpace::computeXYZMatrix( - const std::array<float2, 3>& primaries, const float2& whitePoint) { - const float2& R = primaries[0]; - const float2& G = primaries[1]; - const float2& B = primaries[2]; - const float2& W = whitePoint; - - float oneRxRy = (1 - R.x) / R.y; - float oneGxGy = (1 - G.x) / G.y; - float oneBxBy = (1 - B.x) / B.y; - float oneWxWy = (1 - W.x) / W.y; - - float RxRy = R.x / R.y; - float GxGy = G.x / G.y; - float BxBy = B.x / B.y; - float WxWy = W.x / W.y; - - float BY = - ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) / - ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy)); - float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy); - float RY = 1 - GY - BY; - - float RYRy = RY / R.y; - float GYGy = GY / G.y; - float BYBy = BY / B.y; - - return { - float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)}, - float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)}, - float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)} - }; -} - -const ColorSpace ColorSpace::sRGB() { - return { - "sRGB IEC61966-2.1", - {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, - {0.3127f, 0.3290f}, - {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f} - }; -} - -const ColorSpace ColorSpace::linearSRGB() { - return { - "sRGB IEC61966-2.1 (Linear)", - {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, - {0.3127f, 0.3290f} - }; -} - -const ColorSpace ColorSpace::extendedSRGB() { - return { - "scRGB-nl IEC 61966-2-2:2003", - {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, - {0.3127f, 0.3290f}, - std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), - std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), - std::bind(clamp<float>, _1, -0.799f, 2.399f) - }; -} - -const ColorSpace ColorSpace::linearExtendedSRGB() { - return { - "scRGB IEC 61966-2-2:2003", - {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, - {0.3127f, 0.3290f}, - 1.0f, - std::bind(clamp<float>, _1, -0.5f, 7.499f) - }; -} - -const ColorSpace ColorSpace::NTSC() { - return { - "NTSC (1953)", - {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, - {0.310f, 0.316f}, - {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} - }; -} - -const ColorSpace ColorSpace::BT709() { - return { - "Rec. ITU-R BT.709-5", - {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, - {0.3127f, 0.3290f}, - {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} - }; -} - -const ColorSpace ColorSpace::BT2020() { - return { - "Rec. ITU-R BT.2020-1", - {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, - {0.3127f, 0.3290f}, - {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} - }; -} - -const ColorSpace ColorSpace::AdobeRGB() { - return { - "Adobe RGB (1998)", - {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, - {0.3127f, 0.3290f}, - 2.2f - }; -} - -const ColorSpace ColorSpace::ProPhotoRGB() { - return { - "ROMM RGB ISO 22028-2:2013", - {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, - {0.34567f, 0.35850f}, - {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f} - }; -} - -const ColorSpace ColorSpace::DisplayP3() { - return { - "Display P3", - {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, - {0.3127f, 0.3290f}, - {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f} - }; -} - -const ColorSpace ColorSpace::DCIP3() { - return { - "SMPTE RP 431-2-2007 DCI (P3)", - {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, - {0.314f, 0.351f}, - 2.6f - }; -} - -const ColorSpace ColorSpace::ACES() { - return { - "SMPTE ST 2065-1:2012 ACES", - {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, - {0.32168f, 0.33767f}, - 1.0f, - std::bind(clamp<float>, _1, -65504.0f, 65504.0f) - }; -} - -const ColorSpace ColorSpace::ACEScg() { - return { - "Academy S-2014-004 ACEScg", - {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, - {0.32168f, 0.33767f}, - 1.0f, - std::bind(clamp<float>, _1, -65504.0f, 65504.0f) - }; -} - -std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, - const ColorSpace& dst) { - size = clamp(size, 2u, 256u); - float m = 1.0f / float(size - 1); - - std::unique_ptr<float3[]> lut(new float3[size * size * size]); - float3* data = lut.get(); - - ColorSpaceConnector connector(src, dst); - - for (uint32_t z = 0; z < size; z++) { - for (int32_t y = int32_t(size - 1); y >= 0; y--) { - for (uint32_t x = 0; x < size; x++) { - *data++ = connector.transform({x * m, y * m, z * m}); - } - } - } - - return lut; -} - -static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; -static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; -static const mat3 BRADFORD = mat3{ - float3{ 0.8951f, -0.7502f, 0.0389f}, - float3{ 0.2664f, 1.7135f, -0.0685f}, - float3{-0.1614f, 0.0367f, 1.0296f} -}; - -static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) { - float3 srcLMS = matrix * srcWhitePoint; - float3 dstLMS = matrix * dstWhitePoint; - return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; -} - -ColorSpaceConnector::ColorSpaceConnector( - const ColorSpace& src, - const ColorSpace& dst) noexcept - : mSource(src) - , mDestination(dst) { - - if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) { - mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ(); - } else { - mat3 rgbToXYZ(src.getRGBtoXYZ()); - mat3 xyzToRGB(dst.getXYZtoRGB()); - - float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1}); - float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1}); - - if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { - rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); - } - - if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { - xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ()); - } - - mTransform = xyzToRGB * rgbToXYZ; - } -} - -}; // namespace android diff --git a/location/api/current.txt b/location/api/current.txt index c55676bc1e78..c7954feb9d4f 100644 --- a/location/api/current.txt +++ b/location/api/current.txt @@ -682,6 +682,7 @@ package android.location.altitude { public final class AltitudeConverter { ctor public AltitudeConverter(); method @WorkerThread public void addMslAltitudeToLocation(@NonNull android.content.Context, @NonNull android.location.Location) throws java.io.IOException; + method @FlaggedApi(Flags.FLAG_GEOID_HEIGHTS_VIA_ALTITUDE_HAL) public boolean addMslAltitudeToLocation(@NonNull android.location.Location); } } diff --git a/location/java/android/location/altitude/AltitudeConverter.java b/location/java/android/location/altitude/AltitudeConverter.java index 3dc024efef56..461dafb91916 100644 --- a/location/java/android/location/altitude/AltitudeConverter.java +++ b/location/java/android/location/altitude/AltitudeConverter.java @@ -16,12 +16,16 @@ package android.location.altitude; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.WorkerThread; import android.content.Context; +import android.frameworks.location.altitude.GetGeoidHeightRequest; +import android.frameworks.location.altitude.GetGeoidHeightResponse; import android.location.Location; +import android.location.flags.Flags; -import com.android.internal.location.altitude.GeoidHeightMap; +import com.android.internal.location.altitude.GeoidMap; import com.android.internal.location.altitude.S2CellIdUtils; import com.android.internal.location.altitude.nano.MapParamsProto; import com.android.internal.util.Preconditions; @@ -37,7 +41,7 @@ import java.io.IOException; * <pre> * Brian Julian and Michael Angermann. * "Resource efficient and accurate altitude conversion to Mean Sea Level." - * To appear in 2023 IEEE/ION Position, Location and Navigation Symposium (PLANS). + * 2023 IEEE/ION Position, Location and Navigation Symposium (PLANS). * </pre> */ public final class AltitudeConverter { @@ -45,8 +49,8 @@ public final class AltitudeConverter { private static final double MAX_ABS_VALID_LATITUDE = 90; private static final double MAX_ABS_VALID_LONGITUDE = 180; - /** Manages a mapping of geoid heights associated with S2 cells. */ - private final GeoidHeightMap mGeoidHeightMap = new GeoidHeightMap(); + /** Manages a mapping of geoid heights and expiration distances associated with S2 cells. */ + private final GeoidMap mGeoidMap = new GeoidMap(); /** * Creates an instance that manages an independent cache to optimized conversions of locations @@ -78,75 +82,87 @@ public final class AltitudeConverter { /** * Returns the four S2 cell IDs for the map square associated with the {@code location}. * - * <p>The first map cell contains the location, while the others are located horizontally, - * vertically, and diagonally, in that order, with respect to the S2 (i,j) coordinate system. If - * the diagonal map cell does not exist (i.e., the location is near an S2 cube vertex), its - * corresponding ID is set to zero. + * <p>The first map cell, denoted z11 in the appendix of the referenced paper above, contains + * the location. The others are the map cells denoted z21, z12, and z22, in that order. */ - @NonNull - private static long[] findMapSquare(@NonNull MapParamsProto params, + private static long[] findMapSquare(@NonNull MapParamsProto geoidHeightParams, @NonNull Location location) { long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(), location.getLongitude()); // Cell-space properties and coordinates. - int sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - params.mapS2Level); + int sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - geoidHeightParams.mapS2Level); int maxIj = 1 << S2CellIdUtils.MAX_LEVEL; - long s0 = S2CellIdUtils.getParent(s2CellId, params.mapS2Level); - int f0 = S2CellIdUtils.getFace(s2CellId); - int i0 = S2CellIdUtils.getI(s2CellId); - int j0 = S2CellIdUtils.getJ(s2CellId); - int i1 = i0 + sizeIj; - int j1 = j0 + sizeIj; + long z11 = S2CellIdUtils.getParent(s2CellId, geoidHeightParams.mapS2Level); + int f11 = S2CellIdUtils.getFace(s2CellId); + int i1 = S2CellIdUtils.getI(s2CellId); + int j1 = S2CellIdUtils.getJ(s2CellId); + int i2 = i1 + sizeIj; + int j2 = j1 + sizeIj; // Non-boundary region calculation - simplest and most common case. - if (i1 < maxIj && j1 < maxIj) { - return new long[]{ - s0, - S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f0, i1, j0), params.mapS2Level), - S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f0, i0, j1), params.mapS2Level), - S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f0, i1, j1), params.mapS2Level) - }; + if (i2 < maxIj && j2 < maxIj) { + return new long[]{z11, S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f11, i2, j1), + geoidHeightParams.mapS2Level), S2CellIdUtils.getParent( + S2CellIdUtils.fromFij(f11, i1, j2), geoidHeightParams.mapS2Level), + S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f11, i2, j2), + geoidHeightParams.mapS2Level)}; } - // Boundary region calculation. + // Boundary region calculation long[] edgeNeighbors = new long[4]; - S2CellIdUtils.getEdgeNeighbors(s0, edgeNeighbors); - long s1 = edgeNeighbors[1]; - long s2 = edgeNeighbors[2]; - long s3; - if (f0 % 2 == 1) { - S2CellIdUtils.getEdgeNeighbors(s1, edgeNeighbors); - if (i1 < maxIj) { - s3 = edgeNeighbors[2]; - } else { - s3 = s1; - s1 = edgeNeighbors[1]; - } - } else { - S2CellIdUtils.getEdgeNeighbors(s2, edgeNeighbors); - if (j1 < maxIj) { - s3 = edgeNeighbors[1]; - } else { - s3 = s2; - s2 = edgeNeighbors[3]; - } - } + S2CellIdUtils.getEdgeNeighbors(z11, edgeNeighbors); + long z11W = edgeNeighbors[0]; + long z11S = edgeNeighbors[1]; + long z11E = edgeNeighbors[2]; + long z11N = edgeNeighbors[3]; + + long[] otherEdgeNeighbors = new long[4]; + S2CellIdUtils.getEdgeNeighbors(z11W, otherEdgeNeighbors); + S2CellIdUtils.getEdgeNeighbors(z11S, edgeNeighbors); + long z11Sw = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11); + S2CellIdUtils.getEdgeNeighbors(z11E, otherEdgeNeighbors); + long z11Se = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11); + S2CellIdUtils.getEdgeNeighbors(z11N, edgeNeighbors); + long z11Ne = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11); + + long z21 = (f11 % 2 == 1 && i2 >= maxIj) ? z11Sw : z11S; + long z12 = (f11 % 2 == 0 && j2 >= maxIj) ? z11Ne : z11E; + long z22 = (z21 == z11Sw) ? z11S : (z12 == z11Ne) ? z11E : z11Se; // Reuse edge neighbors' array to avoid an extra allocation. - edgeNeighbors[0] = s0; - edgeNeighbors[1] = s1; - edgeNeighbors[2] = s2; - edgeNeighbors[3] = s3; + edgeNeighbors[0] = z11; + edgeNeighbors[1] = z21; + edgeNeighbors[2] = z12; + edgeNeighbors[3] = z22; return edgeNeighbors; } /** + * Returns the first common non-z11 neighbor found between the two arrays of edge neighbors. If + * such a common neighbor does not exist, returns z11. + */ + private static long findCommonNeighbor(long[] edgeNeighbors, long[] otherEdgeNeighbors, + long z11) { + for (long edgeNeighbor : edgeNeighbors) { + if (edgeNeighbor == z11) { + continue; + } + for (long otherEdgeNeighbor : otherEdgeNeighbors) { + if (edgeNeighbor == otherEdgeNeighbor) { + return edgeNeighbor; + } + } + } + return z11; + } + + /** * Adds to {@code location} the bilinearly interpolated Mean Sea Level altitude. In addition, a * Mean Sea Level altitude accuracy is added if the {@code location} has a valid vertical * accuracy; otherwise, does not add a corresponding accuracy. */ - private static void addMslAltitude(@NonNull MapParamsProto params, + private static void addMslAltitude(@NonNull MapParamsProto geoidHeightParams, @NonNull double[] geoidHeightsMeters, @NonNull Location location) { double h0 = geoidHeightsMeters[0]; double h1 = geoidHeightsMeters[1]; @@ -158,7 +174,7 @@ public final class AltitudeConverter { // employ the simplified unit square formulation. long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(), location.getLongitude()); - double sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - params.mapS2Level); + double sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - geoidHeightParams.mapS2Level); double wi = (S2CellIdUtils.getI(s2CellId) % sizeIj) / sizeIj; double wj = (S2CellIdUtils.getJ(s2CellId) % sizeIj) / sizeIj; double offsetMeters = h0 + (h1 - h0) * wi + (h2 - h0) * wj + (h3 - h1 - h2 + h0) * wi * wj; @@ -167,8 +183,8 @@ public final class AltitudeConverter { if (location.hasVerticalAccuracy()) { double verticalAccuracyMeters = location.getVerticalAccuracyMeters(); if (Double.isFinite(verticalAccuracyMeters) && verticalAccuracyMeters >= 0) { - location.setMslAltitudeAccuracyMeters( - (float) Math.hypot(verticalAccuracyMeters, params.modelRmseMeters)); + location.setMslAltitudeAccuracyMeters((float) Math.hypot(verticalAccuracyMeters, + geoidHeightParams.modelRmseMeters)); } } } @@ -191,33 +207,84 @@ public final class AltitudeConverter { public void addMslAltitudeToLocation(@NonNull Context context, @NonNull Location location) throws IOException { validate(location); - MapParamsProto params = GeoidHeightMap.getParams(context); - long[] s2CellIds = findMapSquare(params, location); - double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, context, s2CellIds); - addMslAltitude(params, geoidHeightsMeters, location); + MapParamsProto geoidHeightParams = GeoidMap.getGeoidHeightParams(context); + long[] mapCells = findMapSquare(geoidHeightParams, location); + double[] geoidHeightsMeters = mGeoidMap.readGeoidHeights(geoidHeightParams, context, + mapCells); + addMslAltitude(geoidHeightParams, geoidHeightsMeters, location); } /** - * Same as {@link #addMslAltitudeToLocation(Context, Location)} except that data will not be - * loaded from raw assets. Returns true if a Mean Sea Level altitude is added to the - * {@code location}; otherwise, returns false and leaves the {@code location} unchanged. - * - * @hide + * Same as {@link #addMslAltitudeToLocation(Context, Location)} except that this method can be + * called on the main thread as data will not be loaded from raw assets. Returns true if a Mean + * Sea Level altitude is added to the {@code location}; otherwise, returns false and leaves the + * {@code location} unchanged. */ + @FlaggedApi(Flags.FLAG_GEOID_HEIGHTS_VIA_ALTITUDE_HAL) public boolean addMslAltitudeToLocation(@NonNull Location location) { validate(location); - MapParamsProto params = GeoidHeightMap.getParams(); - if (params == null) { + MapParamsProto geoidHeightParams = GeoidMap.getGeoidHeightParams(); + if (geoidHeightParams == null) { return false; } - long[] s2CellIds = findMapSquare(params, location); - double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, s2CellIds); + long[] mapCells = findMapSquare(geoidHeightParams, location); + double[] geoidHeightsMeters = mGeoidMap.readGeoidHeights(geoidHeightParams, mapCells); if (geoidHeightsMeters == null) { return false; } - addMslAltitude(params, geoidHeightsMeters, location); + addMslAltitude(geoidHeightParams, geoidHeightsMeters, location); return true; } + + /** + * Returns the geoid height (a.k.a. geoid undulation) at the location specified in {@code + * request}. The geoid height at a location is defined as the difference between an altitude + * measured above the World Geodetic System 1984 reference ellipsoid (WGS84) and its + * corresponding Mean Sea Level altitude. + * + * <p>Must be called off the main thread as data may be loaded from raw assets. + * + * @throws IOException if an I/O error occurs when loading data from raw assets. + * @throws IllegalArgumentException if the {@code request} has an invalid latitude or longitude. + * Specifically, the latitude must be between -90 and 90 (both + * inclusive), and the longitude must be between -180 and 180 + * (both inclusive). + * @hide + */ + @WorkerThread + public @NonNull GetGeoidHeightResponse getGeoidHeight(@NonNull Context context, + @NonNull GetGeoidHeightRequest request) throws IOException { + // Create a valid location from which the geoid height and its accuracy will be extracted. + Location location = new Location(""); + location.setLatitude(request.latitudeDegrees); + location.setLongitude(request.longitudeDegrees); + location.setAltitude(0.0); + location.setVerticalAccuracyMeters(0.0f); + + addMslAltitudeToLocation(context, location); + // The geoid height for a location with zero WGS84 altitude is equal in value to the + // negative of corresponding MSL altitude. + double geoidHeightMeters = -location.getMslAltitudeMeters(); + // The geoid height error for a location with zero vertical accuracy is equal in value to + // the corresponding MSL altitude accuracy. + float geoidHeightErrorMeters = location.getMslAltitudeAccuracyMeters(); + + MapParamsProto expirationDistanceParams = GeoidMap.getExpirationDistanceParams(context); + long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(), + location.getLongitude()); + long[] mapCell = {S2CellIdUtils.getParent(s2CellId, expirationDistanceParams.mapS2Level)}; + double expirationDistanceMeters = mGeoidMap.readExpirationDistances( + expirationDistanceParams, context, mapCell)[0]; + float additionalGeoidHeightErrorMeters = (float) expirationDistanceParams.modelRmseMeters; + + GetGeoidHeightResponse response = new GetGeoidHeightResponse(); + response.geoidHeightMeters = geoidHeightMeters; + response.geoidHeightErrorMeters = geoidHeightErrorMeters; + response.expirationDistanceMeters = expirationDistanceMeters; + response.additionalGeoidHeightErrorMeters = additionalGeoidHeightErrorMeters; + response.success = true; + return response; + } } diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/location.aconfig index 8c7c8716b2dc..a96fe47f2381 100644 --- a/location/java/android/location/flags/gnss.aconfig +++ b/location/java/android/location/flags/location.aconfig @@ -1,6 +1,20 @@ package: "android.location.flags" flag { + name: "fix_service_watcher" + namespace: "location" + description: "Enable null explicit services in ServiceWatcher" + bug: "311210517" +} + +flag { + name: "geoid_heights_via_altitude_hal" + namespace: "location" + description: "Flag for making geoid heights available via the Altitude HAL" + bug: "304375846" +} + +flag { name: "gnss_api_navic_l1" namespace: "location" description: "Flag for GNSS API for NavIC L1" diff --git a/location/java/com/android/internal/location/altitude/GeoidHeightMap.java b/location/java/com/android/internal/location/altitude/GeoidMap.java index 8067050d9da3..9bf5689c1028 100644 --- a/location/java/com/android/internal/location/altitude/GeoidHeightMap.java +++ b/location/java/com/android/internal/location/altitude/GeoidMap.java @@ -34,11 +34,12 @@ import java.nio.ByteBuffer; import java.util.Objects; /** - * Manages a mapping of geoid heights associated with S2 cells, referred to as MAP CELLS. + * Manages a mapping of geoid heights and expiration distances associated with S2 cells, referred to + * as MAP CELLS. * * <p>Tiles are used extensively to reduce the number of entries needed to be stored in memory and - * on disk. A tile associates geoid heights with all map cells of a common parent at a specified S2 - * level. + * on disk. A tile associates geoid heights or expiration distances with all map cells of a common + * parent at a specified S2 level. * * <p>Since bilinear interpolation considers at most four map cells at a time, at most four tiles * are simultaneously stored in memory. These tiles, referred to as CACHE TILES, are each keyed by @@ -48,42 +49,79 @@ import java.util.Objects; * The latter tiles, referred to as DISK TILES, are each keyed by its common parent's S2 cell token, * referred to as a DISK TOKEN. */ -public final class GeoidHeightMap { +public final class GeoidMap { - private static final Object sLock = new Object(); + private static final Object GEOID_HEIGHT_PARAMS_LOCK = new Object(); - @GuardedBy("sLock") + private static final Object EXPIRATION_DISTANCE_PARAMS_LOCK = new Object(); + + @GuardedBy("GEOID_HEIGHT_PARAMS_LOCK") + @Nullable + private static MapParamsProto sGeoidHeightParams; + + @GuardedBy("EXPIRATION_DISTANCE_PARAMS_LOCK") @Nullable - private static MapParamsProto sParams; + private static MapParamsProto sExpirationDistanceParams; + + /** + * Defines a cache large enough to hold all geoid height cache tiles needed for interpolation. + */ + private final LruCache<Long, S2TileProto> mGeoidHeightCacheTiles = new LruCache<>(4); - /** Defines a cache large enough to hold all cache tiles needed for interpolation. */ - private final LruCache<Long, S2TileProto> mCacheTiles = new LruCache<>(4); + /** + * Defines a cache large enough to hold all expiration distance cache tiles needed for + * interpolation. + */ + private final LruCache<Long, S2TileProto> mExpirationDistanceCacheTiles = new LruCache<>(4); /** - * Returns the singleton parameter instance for a spherically projected geoid height map and its - * corresponding tile management. + * Returns the singleton parameter instance for geoid height parameters of a spherically + * projected map. */ @NonNull - public static MapParamsProto getParams(@NonNull Context context) throws IOException { - synchronized (sLock) { - if (sParams == null) { - try (InputStream is = context.getApplicationContext().getAssets().open( - "geoid_height_map/map-params.pb")) { - sParams = MapParamsProto.parseFrom(is.readAllBytes()); - } + public static MapParamsProto getGeoidHeightParams(@NonNull Context context) throws IOException { + synchronized (GEOID_HEIGHT_PARAMS_LOCK) { + if (sGeoidHeightParams == null) { + // TODO: b/304375846 - Configure with disk tile prefix once resources are updated. + sGeoidHeightParams = parseParams(context); + } + return sGeoidHeightParams; + } + } + + /** + * Returns the singleton parameter instance for expiration distance parameters of a spherically + * projected + * map. + */ + @NonNull + public static MapParamsProto getExpirationDistanceParams(@NonNull Context context) + throws IOException { + synchronized (EXPIRATION_DISTANCE_PARAMS_LOCK) { + if (sExpirationDistanceParams == null) { + // TODO: b/304375846 - Configure with disk tile prefix once resources are updated. + sExpirationDistanceParams = parseParams(context); } - return sParams; + return sExpirationDistanceParams; + } + } + + @NonNull + private static MapParamsProto parseParams(@NonNull Context context) throws IOException { + try (InputStream is = context.getApplicationContext().getAssets().open( + "geoid_height_map/map-params.pb")) { + return MapParamsProto.parseFrom(is.readAllBytes()); } } /** - * Same as {@link #getParams(Context)} except that null is returned if the singleton parameter - * instance is not yet initialized. + * Same as {@link #getGeoidHeightParams(Context)} except that null is returned if the singleton + * parameter instance is not yet initialized. */ @Nullable - public static MapParamsProto getParams() { - synchronized (sLock) { - return sParams; + public static MapParamsProto getGeoidHeightParams() { + synchronized (GEOID_HEIGHT_PARAMS_LOCK) { + return sGeoidHeightParams; } } @@ -93,18 +131,17 @@ public final class GeoidHeightMap { @NonNull private static String getDiskToken(@NonNull MapParamsProto params, long s2CellId) { - return S2CellIdUtils.getToken( - S2CellIdUtils.getParent(s2CellId, params.diskTileS2Level)); + return S2CellIdUtils.getToken(S2CellIdUtils.getParent(s2CellId, params.diskTileS2Level)); } /** * Adds to {@code values} values in the unit interval [0, 1] for the map cells identified by - * {@code s2CellIds}. Returns true if values are present for all IDs; otherwise, returns false - * and adds NaNs for absent values. + * {@code s2CellIds}. Returns true if values are present for all IDs; otherwise, adds NaNs for + * absent values and returns false. */ private static boolean getUnitIntervalValues(@NonNull MapParamsProto params, - @NonNull TileFunction tileFunction, - @NonNull long[] s2CellIds, @NonNull double[] values) { + @NonNull TileFunction tileFunction, @NonNull long[] s2CellIds, + @NonNull double[] values) { int len = s2CellIds.length; S2TileProto[] tiles = new S2TileProto[len]; @@ -137,9 +174,8 @@ public final class GeoidHeightMap { @SuppressWarnings("ReferenceEquality") private static void mergeByteBufferValues(@NonNull MapParamsProto params, - @NonNull long[] s2CellIds, - @NonNull S2TileProto[] tiles, - int tileIndex, @NonNull double[] values) { + @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex, + @NonNull double[] values) { byte[] bytes = tiles[tileIndex].byteBuffer; if (bytes == null || bytes.length == 0) { return; @@ -163,24 +199,22 @@ public final class GeoidHeightMap { } private static void mergeByteJpegValues(@NonNull MapParamsProto params, - @NonNull long[] s2CellIds, - @NonNull S2TileProto[] tiles, - int tileIndex, @NonNull double[] values) { + @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex, + @NonNull double[] values) { mergeByteImageValues(params, tiles[tileIndex].byteJpeg, s2CellIds, tiles, tileIndex, values); } private static void mergeBytePngValues(@NonNull MapParamsProto params, - @NonNull long[] s2CellIds, - @NonNull S2TileProto[] tiles, - int tileIndex, @NonNull double[] values) { + @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex, + @NonNull double[] values) { mergeByteImageValues(params, tiles[tileIndex].bytePng, s2CellIds, tiles, tileIndex, values); } @SuppressWarnings("ReferenceEquality") private static void mergeByteImageValues(@NonNull MapParamsProto params, @NonNull byte[] bytes, - @NonNull long[] s2CellIds, - @NonNull S2TileProto[] tiles, int tileIndex, @NonNull double[] values) { + @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex, + @NonNull double[] values) { if (bytes == null || bytes.length == 0) { return; } @@ -219,7 +253,7 @@ public final class GeoidHeightMap { * ID. */ private static void validate(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) { - Preconditions.checkArgument(s2CellIds.length == 4); + Preconditions.checkArgument(s2CellIds.length <= 4); for (long s2CellId : s2CellIds) { Preconditions.checkArgument(S2CellIdUtils.getLevel(s2CellId) == params.mapS2Level); } @@ -233,15 +267,38 @@ public final class GeoidHeightMap { @NonNull public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull Context context, @NonNull long[] s2CellIds) throws IOException { + return readMapValues(params, context, s2CellIds, mGeoidHeightCacheTiles); + } + + /** + * Returns the expiration distances in meters associated with the map cells identified by + * {@code s2CellIds}. Throws an {@link IOException} if a geoid height cannot be calculated for + * an ID. + */ + @NonNull + public double[] readExpirationDistances(@NonNull MapParamsProto params, + @NonNull Context context, @NonNull long[] s2CellIds) throws IOException { + return readMapValues(params, context, s2CellIds, mExpirationDistanceCacheTiles); + } + + /** + * Returns the map values in meters associated with the map cells identified by + * {@code s2CellIds}. Throws an {@link IOException} if a map value cannot be calculated for an + * ID. + */ + @NonNull + private static double[] readMapValues(@NonNull MapParamsProto params, @NonNull Context context, + @NonNull long[] s2CellIds, @NonNull LruCache<Long, S2TileProto> cacheTiles) + throws IOException { validate(params, s2CellIds); - double[] heightsMeters = new double[s2CellIds.length]; - if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) { - return heightsMeters; + double[] mapValuesMeters = new double[s2CellIds.length]; + if (getMapValues(params, cacheTiles::get, s2CellIds, mapValuesMeters)) { + return mapValuesMeters; } - TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds); - if (getGeoidHeights(params, loadedTiles, s2CellIds, heightsMeters)) { - return heightsMeters; + TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds, cacheTiles); + if (getMapValues(params, loadedTiles, s2CellIds, mapValuesMeters)) { + return mapValuesMeters; } throw new IOException("Unable to calculate geoid heights from raw assets."); } @@ -255,32 +312,33 @@ public final class GeoidHeightMap { public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) { validate(params, s2CellIds); double[] heightsMeters = new double[s2CellIds.length]; - if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) { + if (getMapValues(params, mGeoidHeightCacheTiles::get, s2CellIds, heightsMeters)) { return heightsMeters; } return null; } /** - * Adds to {@code heightsMeters} the geoid heights in meters associated with the map cells + * Adds to {@code mapValuesMeters} the map values in meters associated with the map cells * identified by {@code s2CellIds}. Returns true if heights are present for all IDs; otherwise, - * returns false and adds NaNs for absent heights. + * adds NaNs for absent heights and returns false. */ - private boolean getGeoidHeights(@NonNull MapParamsProto params, + private static boolean getMapValues(@NonNull MapParamsProto params, @NonNull TileFunction tileFunction, @NonNull long[] s2CellIds, - @NonNull double[] heightsMeters) { - boolean allFound = getUnitIntervalValues(params, tileFunction, s2CellIds, heightsMeters); - for (int i = 0; i < heightsMeters.length; i++) { + @NonNull double[] mapValuesMeters) { + boolean allFound = getUnitIntervalValues(params, tileFunction, s2CellIds, mapValuesMeters); + for (int i = 0; i < mapValuesMeters.length; i++) { // NaNs are properly preserved. - heightsMeters[i] *= params.modelAMeters; - heightsMeters[i] += params.modelBMeters; + mapValuesMeters[i] *= params.modelAMeters; + mapValuesMeters[i] += params.modelBMeters; } return allFound; } @NonNull - private TileFunction loadFromCacheAndDisk(@NonNull MapParamsProto params, - @NonNull Context context, @NonNull long[] s2CellIds) throws IOException { + private static TileFunction loadFromCacheAndDisk(@NonNull MapParamsProto params, + @NonNull Context context, @NonNull long[] s2CellIds, + @NonNull LruCache<Long, S2TileProto> cacheTiles) throws IOException { int len = s2CellIds.length; // Enable batch loading by finding all cache keys upfront. @@ -296,7 +354,7 @@ public final class GeoidHeightMap { if (diskTokens[i] != null) { continue; } - loadedTiles[i] = mCacheTiles.get(cacheKeys[i]); + loadedTiles[i] = cacheTiles.get(cacheKeys[i]); diskTokens[i] = getDiskToken(params, cacheKeys[i]); // Batch across common cache key. @@ -319,7 +377,7 @@ public final class GeoidHeightMap { "geoid_height_map/tile-" + diskTokens[i] + ".pb")) { tile = S2TileProto.parseFrom(is.readAllBytes()); } - mergeFromDiskTile(params, tile, cacheKeys, diskTokens, i, loadedTiles); + mergeFromDiskTile(params, tile, cacheKeys, diskTokens, i, loadedTiles, cacheTiles); } return cacheKey -> { @@ -332,9 +390,10 @@ public final class GeoidHeightMap { }; } - private void mergeFromDiskTile(@NonNull MapParamsProto params, @NonNull S2TileProto diskTile, - @NonNull long[] cacheKeys, @NonNull String[] diskTokens, int diskTokenIndex, - @NonNull S2TileProto[] loadedTiles) throws IOException { + private static void mergeFromDiskTile(@NonNull MapParamsProto params, + @NonNull S2TileProto diskTile, @NonNull long[] cacheKeys, @NonNull String[] diskTokens, + int diskTokenIndex, @NonNull S2TileProto[] loadedTiles, + @NonNull LruCache<Long, S2TileProto> cacheTiles) throws IOException { int len = cacheKeys.length; int numMapCellsPerCacheTile = 1 << (2 * (params.mapS2Level - params.cacheTileS2Level)); @@ -375,7 +434,7 @@ public final class GeoidHeightMap { } // Side load into tile cache. - mCacheTiles.put(cacheKeys[i], loadedTiles[i]); + cacheTiles.put(cacheKeys[i], loadedTiles[i]); } } diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 470a8ac279c7..bfb4b42a8346 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -17,6 +17,7 @@ package android.media; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -43,21 +44,25 @@ import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ReadOnlyBufferException; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME; /** MediaCodec class can be used to access low-level media codecs, i.e. encoder/decoder components. It is part of the Android low-level multimedia support infrastructure (normally used together @@ -1824,6 +1829,7 @@ final public class MediaCodec { private static final String EOS_AND_DECODE_ONLY_ERROR_MESSAGE = "An input buffer cannot have " + "both BUFFER_FLAG_END_OF_STREAM and BUFFER_FLAG_DECODE_ONLY flags"; private static final int CB_CRYPTO_ERROR = 6; + private static final int CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7; private class EventHandler extends Handler { private MediaCodec mCodec; @@ -1945,6 +1951,39 @@ final public class MediaCodec { break; } + case CB_LARGE_FRAME_OUTPUT_AVAILABLE: + { + int index = msg.arg2; + ArrayDeque<BufferInfo> infos = (ArrayDeque<BufferInfo>)msg.obj; + synchronized(mBufferLock) { + switch (mBufferMode) { + case BUFFER_MODE_LEGACY: + validateOutputByteBuffersLocked(mCachedOutputBuffers, + index, infos); + break; + case BUFFER_MODE_BLOCK: + while (mOutputFrames.size() <= index) { + mOutputFrames.add(null); + } + OutputFrame frame = mOutputFrames.get(index); + if (frame == null) { + frame = new OutputFrame(index); + mOutputFrames.set(index, frame); + } + frame.setBufferInfos(infos); + frame.setAccessible(true); + break; + default: + throw new IllegalArgumentException( + "Unrecognized buffer mode: for large frame output"); + } + } + mCallback.onOutputBuffersAvailable( + mCodec, index, infos); + + break; + } + case CB_ERROR: { mCallback.onError(mCodec, (MediaCodec.CodecException) msg.obj); @@ -2836,11 +2875,72 @@ final public class MediaCodec { } } + /** + * Submit multiple access units to the codec along with multiple + * {@link MediaCodec.BufferInfo} describing the contents of the buffer. This method + * is supported only in asynchronous mode. While this method can be used for all codecs, + * it is meant for buffer batching, which is only supported by codecs that advertise + * FEATURE_MultipleFrames. Other codecs will not output large output buffers via + * onOutputBuffersAvailable, and instead will output single-access-unit output via + * onOutputBufferAvailable. + * <p> + * Output buffer size can be configured using the following MediaFormat keys. + * {@link MediaFormat#KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE} and + * {@link MediaFormat#KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE}. + * Details for each access unit present in the buffer should be described using + * {@link MediaCodec.BufferInfo}. Access units must be laid out contiguously (without any gaps) + * and in order. Multiple access units in the output if present, will be available in + * {@link Callback#onOutputBuffersAvailable} or {@link Callback#onOutputBufferAvailable} + * in case of single-access-unit output or when output does not contain any buffers, + * such as flags. + * <p> + * All other details for populating {@link MediaCodec.BufferInfo} is the same as described in + * {@link #queueInputBuffer}. + * + * @param index The index of a client-owned input buffer previously returned + * in a call to {@link #dequeueInputBuffer}. + * @param bufferInfos ArrayDeque of {@link MediaCodec.BufferInfo} that describes the + * contents in the buffer. The ArrayDeque and the BufferInfo objects provided + * can be recycled by the caller for re-use. + * @throws IllegalStateException if not in the Executing state or not in asynchronous mode. + * @throws MediaCodec.CodecException upon codec error. + * @throws IllegalArgumentException upon if bufferInfos is empty, contains null, or if the + * access units are not contiguous. + * @throws CryptoException if a crypto object has been specified in + * {@link #configure} + */ + @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) + public final void queueInputBuffers( + int index, + @NonNull ArrayDeque<BufferInfo> bufferInfos) { + synchronized(mBufferLock) { + if (mBufferMode == BUFFER_MODE_BLOCK) { + throw new IncompatibleWithBlockModelException("queueInputBuffers() " + + "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. " + + "Please use getQueueRequest() to queue buffers"); + } + invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */); + mDequeuedInputBuffers.remove(index); + } + try { + native_queueInputBuffers( + index, bufferInfos.toArray()); + } catch (CryptoException | IllegalStateException | IllegalArgumentException e) { + revalidateByteBuffer(mCachedInputBuffers, index, true /* input */); + throw e; + } + } + private native final void native_queueInputBuffer( int index, int offset, int size, long presentationTimeUs, int flags) throws CryptoException; + private native final void native_queueInputBuffers( + int index, + @NonNull Object[] infos) + throws CryptoException, CodecException; + public static final int CRYPTO_MODE_UNENCRYPTED = 0; public static final int CRYPTO_MODE_AES_CTR = 1; public static final int CRYPTO_MODE_AES_CBC = 2; @@ -3464,6 +3564,26 @@ final public class MediaCodec { } /** + * Sets MediaCodec.BufferInfo objects describing the access units + * contained in this queue request. Access units must be laid out + * contiguously without gaps and in order. + * + * @param infos Represents {@link MediaCodec.BufferInfo} objects to mark + * individual access-unit boundaries and the timestamps associated with it. + * The buffer is expected to contain the data in a continuous manner. + * @return this object + */ + @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) + public @NonNull QueueRequest setBufferInfos(@NonNull ArrayDeque<BufferInfo> infos) { + if (!isAccessible()) { + throw new IllegalStateException("The request is stale"); + } + mBufferInfos.clear(); + mBufferInfos.addAll(infos); + return this; + } + + /** * Add an integer parameter. * See {@link MediaFormat} for an exhaustive list of supported keys with * values of type int, that can also be set with {@link MediaFormat#setInteger}. @@ -3579,10 +3699,18 @@ final public class MediaCodec { throw new IllegalStateException("No block is set"); } setAccessible(false); + if (mBufferInfos.isEmpty()) { + BufferInfo info = new BufferInfo(); + info.size = mSize; + info.offset = mOffset; + info.presentationTimeUs = mPresentationTimeUs; + info.flags = mFlags; + mBufferInfos.add(info); + } if (mLinearBlock != null) { mCodec.native_queueLinearBlock( - mIndex, mLinearBlock, mOffset, mSize, mCryptoInfo, - mPresentationTimeUs, mFlags, + mIndex, mLinearBlock, mCryptoInfo, + mBufferInfos.toArray(), mTuningKeys, mTuningValues); } else if (mHardwareBuffer != null) { mCodec.native_queueHardwareBuffer( @@ -3600,6 +3728,7 @@ final public class MediaCodec { mHardwareBuffer = null; mPresentationTimeUs = 0; mFlags = 0; + mBufferInfos.clear(); mTuningKeys.clear(); mTuningValues.clear(); return this; @@ -3623,6 +3752,7 @@ final public class MediaCodec { private HardwareBuffer mHardwareBuffer = null; private long mPresentationTimeUs = 0; private @BufferFlag int mFlags = 0; + private final ArrayDeque<BufferInfo> mBufferInfos = new ArrayDeque<>(); private final ArrayList<String> mTuningKeys = new ArrayList<>(); private final ArrayList<Object> mTuningValues = new ArrayList<>(); @@ -3632,11 +3762,8 @@ final public class MediaCodec { private native void native_queueLinearBlock( int index, @NonNull LinearBlock block, - int offset, - int size, @Nullable CryptoInfo cryptoInfo, - long presentationTimeUs, - int flags, + @NonNull Object[] bufferInfos, @NonNull ArrayList<String> keys, @NonNull ArrayList<Object> values); @@ -4050,6 +4177,27 @@ final public class MediaCodec { } } + private void validateOutputByteBuffersLocked( + @Nullable ByteBuffer[] buffers, int index, @NonNull ArrayDeque<BufferInfo> infoDeque) { + Optional<BufferInfo> minInfo = infoDeque.stream().min( + (info1, info2) -> Integer.compare(info1.offset, info2.offset)); + Optional<BufferInfo> maxInfo = infoDeque.stream().max( + (info1, info2) -> Integer.compare(info1.offset, info2.offset)); + if (buffers == null) { + if (index >= 0) { + mValidOutputIndices.set(index); + } + } else if (index >= 0 && index < buffers.length) { + ByteBuffer buffer = buffers[index]; + if (buffer != null && minInfo.isPresent() && maxInfo.isPresent()) { + buffer.setAccessible(true); + buffer.limit(maxInfo.get().offset + maxInfo.get().size); + buffer.position(minInfo.get().offset); + } + } + + } + private void validateOutputByteBufferLocked( @Nullable ByteBuffer[] buffers, int index, @NonNull BufferInfo info) { if (buffers == null) { @@ -4407,6 +4555,22 @@ final public class MediaCodec { return mFlags; } + /* + * Returns the BufferInfos associated with this OutputFrame. These BufferInfos + * describes the access units present in the OutputFrame. Access units are laid + * out contiguously without gaps and in order. + */ + @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) + public @NonNull ArrayDeque<BufferInfo> getBufferInfos() { + if (mBufferInfos.isEmpty()) { + // single BufferInfo could be present. + BufferInfo bufferInfo = new BufferInfo(); + bufferInfo.set(0, 0, mPresentationTimeUs, mFlags); + mBufferInfos.add(bufferInfo); + } + return mBufferInfos; + } + /** * Returns a read-only {@link MediaFormat} for this frame. The returned * object is valid only until the client calls {@link MediaCodec#releaseOutputBuffer}. @@ -4432,6 +4596,7 @@ final public class MediaCodec { mLinearBlock = null; mHardwareBuffer = null; mFormat = null; + mBufferInfos.clear(); mChangedKeys.clear(); mKeySet.clear(); mLoaded = false; @@ -4450,6 +4615,11 @@ final public class MediaCodec { mFlags = info.flags; } + void setBufferInfos(ArrayDeque<BufferInfo> infos) { + mBufferInfos.clear(); + mBufferInfos.addAll(infos); + } + boolean isLoaded() { return mLoaded; } @@ -4464,6 +4634,7 @@ final public class MediaCodec { private long mPresentationTimeUs = 0; private @BufferFlag int mFlags = 0; private MediaFormat mFormat = null; + private final ArrayDeque<BufferInfo> mBufferInfos = new ArrayDeque<>(); private final ArrayList<String> mChangedKeys = new ArrayList<>(); private final Set<String> mKeySet = new HashSet<>(); private boolean mAccessible = false; @@ -5172,6 +5343,32 @@ final public class MediaCodec { @NonNull MediaCodec codec, int index, @NonNull BufferInfo info); /** + * Called when multiple access-units are available in the output. + * + * @param codec The MediaCodec object. + * @param index The index of the available output buffer. + * @param infos Infos describing the available output buffer {@link MediaCodec.BufferInfo}. + * Access units present in the output buffer are laid out contiguously + * without gaps and in order. + */ + @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) + public void onOutputBuffersAvailable( + @NonNull MediaCodec codec, int index, @NonNull ArrayDeque<BufferInfo> infos) { + /* + * This callback returns multiple BufferInfos when codecs are configured to operate on + * large audio frame. Since at this point, we have a single large buffer, returning + * each BufferInfo using + * {@link Callback#onOutputBufferAvailable onOutputBufferAvailable} may cause the + * index to be released to the codec using {@link MediaCodec#releaseOutputBuffer} + * before all BuffersInfos can be returned to the client. + * Hence this callback is required to be implemented or else an exception is thrown. + */ + throw new IllegalStateException( + "Client must override onOutputBuffersAvailable when codec is " + + "configured to operate with multiple access units"); + } + + /** * Called when the MediaCodec encountered an error * * @param codec The MediaCodec object. diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java index 30f4562bbf28..80b606c9ec9e 100644 --- a/media/java/android/media/MediaMuxer.java +++ b/media/java/android/media/MediaMuxer.java @@ -514,7 +514,7 @@ final public class MediaMuxer { * The following table summarizes support for specific format keys across android releases. * Keys marked with '+:' are required. * - * <table style="width: 0%"> + * <table> * <thead> * <tr> * <th rowspan=2>OS Version(s)</th> @@ -583,7 +583,7 @@ final public class MediaMuxer { * <p> * The following table summarizes codec support for containers across android releases: * - * <table style="width: 0%"> + * <table> * <thead> * <tr> * <th rowspan=2>OS Version(s)</th> diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index aa307485c32e..bdfa63010adc 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -65,7 +65,8 @@ import java.util.concurrent.Executor; * * <p>A common case of using MediaRecorder to record audio works as follows: * - * <pre>MediaRecorder recorder = new MediaRecorder(); + * <pre> + * MediaRecorder recorder = new MediaRecorder(context); * recorder.setAudioSource(MediaRecorder.AudioSource.MIC); * recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); * recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl index 388b2c5ca6fe..2fb0af5557b5 100644 --- a/media/java/android/media/projection/IMediaProjection.aidl +++ b/media/java/android/media/projection/IMediaProjection.aidl @@ -18,6 +18,7 @@ package android.media.projection; import android.media.projection.IMediaProjectionCallback; import android.os.IBinder; +import android.app.ActivityOptions.LaunchCookie; /** {@hide} */ interface IMediaProjection { @@ -38,22 +39,22 @@ interface IMediaProjection { void unregisterCallback(IMediaProjectionCallback callback); /** - * Returns the {@link android.os.IBinder} identifying the task to record, or {@code null} if + * Returns the {@link LaunchCookie} identifying the task to record, or {@code null} if * there is none. */ @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IBinder getLaunchCookie(); + LaunchCookie getLaunchCookie(); /** - * Updates the {@link android.os.IBinder} identifying the task to record, or {@code null} if + * Updates the {@link LaunchCookie} identifying the task to record, or {@code null} if * there is none. */ @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - void setLaunchCookie(in IBinder launchCookie); + void setLaunchCookie(in LaunchCookie launchCookie); /** * Returns {@code true} if this token is still valid. A token is valid as long as the token diff --git a/media/java/android/media/projection/MediaProjectionInfo.java b/media/java/android/media/projection/MediaProjectionInfo.java index c82039297d6e..cd0763d4d459 100644 --- a/media/java/android/media/projection/MediaProjectionInfo.java +++ b/media/java/android/media/projection/MediaProjectionInfo.java @@ -16,7 +16,7 @@ package android.media.projection; -import android.os.IBinder; +import android.app.ActivityOptions.LaunchCookie; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; @@ -27,9 +27,9 @@ import java.util.Objects; public final class MediaProjectionInfo implements Parcelable { private final String mPackageName; private final UserHandle mUserHandle; - private final IBinder mLaunchCookie; + private final LaunchCookie mLaunchCookie; - public MediaProjectionInfo(String packageName, UserHandle handle, IBinder launchCookie) { + public MediaProjectionInfo(String packageName, UserHandle handle, LaunchCookie launchCookie) { mPackageName = packageName; mUserHandle = handle; mLaunchCookie = launchCookie; @@ -38,7 +38,7 @@ public final class MediaProjectionInfo implements Parcelable { public MediaProjectionInfo(Parcel in) { mPackageName = in.readString(); mUserHandle = UserHandle.readFromParcel(in); - mLaunchCookie = in.readStrongBinder(); + mLaunchCookie = LaunchCookie.readFromParcel(in); } public String getPackageName() { @@ -49,7 +49,7 @@ public final class MediaProjectionInfo implements Parcelable { return mUserHandle; } - public IBinder getLaunchCookie() { + public LaunchCookie getLaunchCookie() { return mLaunchCookie; } @@ -72,7 +72,7 @@ public final class MediaProjectionInfo implements Parcelable { public String toString() { return "MediaProjectionInfo{mPackageName=" + mPackageName + ", mUserHandle=" - + mUserHandle + ", mLaunchCookie" + + mUserHandle + ", mLaunchCookie=" + mLaunchCookie + "}"; } @@ -85,7 +85,7 @@ public final class MediaProjectionInfo implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(mPackageName); UserHandle.writeToParcel(mUserHandle, out); - out.writeStrongBinder(mLaunchCookie); + LaunchCookie.writeToParcel(mLaunchCookie, out); } public static final @android.annotation.NonNull Parcelable.Creator<MediaProjectionInfo> CREATOR = diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java index 9790d02025b7..e3290d604794 100644 --- a/media/java/android/media/projection/MediaProjectionManager.java +++ b/media/java/android/media/projection/MediaProjectionManager.java @@ -18,8 +18,11 @@ package android.media.projection; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemService; +import android.annotation.TestApi; import android.app.Activity; +import android.app.ActivityOptions.LaunchCookie; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -73,6 +76,9 @@ public final class MediaProjectionManager { /** @hide */ public static final String EXTRA_MEDIA_PROJECTION = "android.media.projection.extra.EXTRA_MEDIA_PROJECTION"; + /** @hide */ + public static final String EXTRA_LAUNCH_COOKIE = + "android.media.projection.extra.EXTRA_LAUNCH_COOKIE"; /** @hide */ public static final int TYPE_SCREEN_CAPTURE = 0; @@ -158,17 +164,29 @@ public final class MediaProjectionManager { */ @NonNull public Intent createScreenCaptureIntent(@NonNull MediaProjectionConfig config) { - Intent i = new Intent(); - final ComponentName mediaProjectionPermissionDialogComponent = - ComponentName.unflattenFromString(mContext.getResources() - .getString(com.android.internal.R.string - .config_mediaProjectionPermissionDialogComponent)); - i.setComponent(mediaProjectionPermissionDialogComponent); + Intent i = createScreenCaptureIntent(); i.putExtra(EXTRA_MEDIA_PROJECTION_CONFIG, config); return i; } /** + * Returns an intent similar to {@link #createScreenCaptureIntent()} that will enable screen + * recording of the task with the specified launch cookie. This method should only be used for + * testing. + * + * @param launchCookie the launch cookie corresponding to the task to record. + * @hide + */ + @SuppressLint("UnflaggedApi") + @TestApi + @NonNull + public Intent createScreenCaptureIntent(@Nullable LaunchCookie launchCookie) { + Intent i = createScreenCaptureIntent(); + i.putExtra(EXTRA_LAUNCH_COOKIE, launchCookie); + return i; + } + + /** * Retrieves the {@link MediaProjection} obtained from a successful screen * capture request. The result code and data from the request are provided by overriding * {@link Activity#onActivityResult(int, int, Intent) onActivityResult(int, int, Intent)}, diff --git a/media/java/android/media/tv/SignalingDataRequest.aidl b/media/java/android/media/tv/SignalingDataRequest.aidl new file mode 100644 index 000000000000..29e89fe4142b --- /dev/null +++ b/media/java/android/media/tv/SignalingDataRequest.aidl @@ -0,0 +1,3 @@ +package android.media.tv; + +parcelable SignalingDataRequest; diff --git a/media/java/android/media/tv/SignalingDataRequest.java b/media/java/android/media/tv/SignalingDataRequest.java new file mode 100644 index 000000000000..dcf1d48aaf3a --- /dev/null +++ b/media/java/android/media/tv/SignalingDataRequest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv; + +import android.annotation.NonNull; +import android.os.Parcelable; + +/** + * Request to retrieve the Low-level Signalling Tables (LLS) and Service-layer Signalling (SLS) + * metadata. + * + * <p>For more details on each type of metadata that can be requested, refer to the ATSC standard + * A/344:2023-5 9.2.10 - Query Signaling Data API. + * + * @hide + */ +public class SignalingDataRequest extends BroadcastInfoRequest implements Parcelable { + private static final @TvInputManager.BroadcastInfoType int REQUEST_TYPE = + TvInputManager.BROADCAST_INFO_TYPE_SIGNALING_DATA; + + public static final @NonNull Parcelable.Creator<SignalingDataRequest> CREATOR = + new Parcelable.Creator<SignalingDataRequest>() { + @Override + public SignalingDataRequest[] newArray(int size) { + return new SignalingDataRequest[size]; + } + + @Override + public SignalingDataRequest createFromParcel(@NonNull android.os.Parcel in) { + return new SignalingDataRequest(in); + } + }; + + /** SLS Metadata: All metadata objects for the requested service(s) */ + public static final int SLS_METADATA_ALL = 0x7FFFFFF; + + /** SLS Metadata: APD for the requested service(s) */ + public static final int SLS_METADATA_APD = 1; + + /** SLS Metadata: USBD for the requested service(s) */ + public static final int SLS_METADATA_USBD = 1 << 1; + + /** SLS Metadata: S-TSID for the requested service(s) */ + public static final int SLS_METADATA_STSID = 1 << 2; + + /** SLS Metadata: DASH MPD for the requested service(s) */ + public static final int SLS_METADATA_MPD = 1 << 3; + + /** SLS Metadata: User Service Description for MMTP */ + public static final int SLS_METADATA_USD = 1 << 4; + + /** SLS Metadata: MMT Package Access Table for the requested service(s) */ + public static final int SLS_METADATA_PAT = 1 << 5; + + /** SLS Metadata: MMT Package Table for the requested service(s) */ + public static final int SLS_METADATA_MPT = 1 << 6; + + /** SLS Metadata: MMT Media Presentation Information Table for the requested service(s) */ + public static final int SLS_METADATA_MPIT = 1 << 7; + + /** SLS Metadata: MMT Clock Relation Information for the requested service(s) */ + public static final int SLS_METADATA_CRIT = 1 << 8; + + /** SLS Metadata: MMT Device Capabilities Information Table for the requested service(s) */ + public static final int SLS_METADATA_DCIT = 1 << 9; + + /** SLS Metadata: HTML Entry Pages Location Description for the requested service(s) */ + public static final int SLS_METADATA_HELD = 1 << 10; + + /** SLS Metadata: Distribution Window Desciription for the requested service(s) */ + public static final int SLS_METADATA_DWD = 1 << 11; + + /** SLS Metadata: MMT Application Event Information for the requested service(s) */ + public static final int SLS_METADATA_AEI = 1 << 12; + + /** SLS Metadata: Video Stream Properties Descriptor */ + public static final int SLS_METADATA_VSPD = 1 << 13; + + /** SLS Metadata: ATSC Staggercast Descriptor */ + public static final int SLS_METADATA_ASD = 1 << 14; + + /** SLS Metadata: Inband Event Descriptor */ + public static final int SLS_METADATA_IED = 1 << 15; + + /** SLS Metadata: Caption Asset Descriptor */ + public static final int SLS_METADATA_CAD = 1 << 16; + + /** SLS Metadata: Audio Stream Properties Descriptor */ + public static final int SLS_METADATA_ASPD = 1 << 17; + + /** SLS Metadata: Security Properties Descriptor */ + public static final int SLS_METADATA_SSD = 1 << 18; + + /** SLS Metadata: ROUTE/DASH Application Dynamic Event for the requested service(s) */ + public static final int SLS_METADATA_EMSG = 1 << 19; + + /** SLS Metadata: MMT Application Dynamic Event for the requested service(s) */ + public static final int SLS_METADATA_EVTI = 1 << 20; + + /** Regional Service Availability Table for the requested service(s) */ + public static final int SLS_METADATA_RSAT = 1 << 21; + + private final int mGroup; + private @NonNull final int[] mLlsTableIds; + private final int mSlsMetadataTypes; + + SignalingDataRequest( + int requestId, + int option, + int group, + @NonNull int[] llsTableIds, + int slsMetadataTypes) { + super(REQUEST_TYPE, requestId, option); + mGroup = group; + mLlsTableIds = llsTableIds; + mSlsMetadataTypes = slsMetadataTypes; + } + + SignalingDataRequest(@NonNull android.os.Parcel in) { + super(REQUEST_TYPE, in); + + int group = in.readInt(); + int[] llsTableIds = in.createIntArray(); + int slsMetadataTypes = in.readInt(); + + this.mGroup = group; + this.mLlsTableIds = llsTableIds; + com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, mLlsTableIds); + this.mSlsMetadataTypes = slsMetadataTypes; + } + + public int getGroup() { + return mGroup; + } + + public @NonNull int[] getLlsTableIds() { + return mLlsTableIds; + } + + public int getSlsMetadataTypes() { + return mSlsMetadataTypes; + } + + @Override + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mGroup); + dest.writeIntArray(mLlsTableIds); + dest.writeInt(mSlsMetadataTypes); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 2b31bfef412e..be1b67547103 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -489,10 +489,19 @@ public final class TvInputManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "BROADCAST_INFO_TYPE_", value = - {BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION, - BROADCAST_INFO_TYPE_PES, BROADCAST_INFO_STREAM_EVENT, BROADCAST_INFO_TYPE_DSMCC, - BROADCAST_INFO_TYPE_COMMAND, BROADCAST_INFO_TYPE_TIMELINE}) + @IntDef( + prefix = "BROADCAST_INFO_TYPE_", + value = { + BROADCAST_INFO_TYPE_TS, + BROADCAST_INFO_TYPE_TABLE, + BROADCAST_INFO_TYPE_SECTION, + BROADCAST_INFO_TYPE_PES, + BROADCAST_INFO_STREAM_EVENT, + BROADCAST_INFO_TYPE_DSMCC, + BROADCAST_INFO_TYPE_COMMAND, + BROADCAST_INFO_TYPE_TIMELINE, + BROADCAST_INFO_TYPE_SIGNALING_DATA + }) public @interface BroadcastInfoType {} public static final int BROADCAST_INFO_TYPE_TS = 1; @@ -505,6 +514,9 @@ public final class TvInputManager { public static final int BROADCAST_INFO_TYPE_TIMELINE = 8; /** @hide */ + public static final int BROADCAST_INFO_TYPE_SIGNALING_DATA = 9; + + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "SIGNAL_STRENGTH_", value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG}) diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl index 7b5853169923..1404d7c9841c 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl @@ -63,6 +63,8 @@ oneway interface ITvInteractiveAppClient { void onRequestTvRecordingInfoList(in int type, int seq); void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data, int seq); + void onRequestSigning2(in String id, in String algorithm, in String host, + int port, in byte[] data, int seq); void onRequestCertificate(in String host, int port, int seq); void onAdRequest(in AdRequest request, int Seq); } diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl index cb89181fd714..3c91a3eeb1dc 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl @@ -61,6 +61,7 @@ oneway interface ITvInteractiveAppSessionCallback { void onRequestTvRecordingInfo(in String recordingId); void onRequestTvRecordingInfoList(in int type); void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data); + void onRequestSigning2(in String id, in String algorithm, in String host, int port, in byte[] data); void onRequestCertificate(in String host, int port); void onAdRequest(in AdRequest request); } diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 011744f94edb..498eec604b9c 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java @@ -657,6 +657,19 @@ public final class TvInteractiveAppManager { } @Override + public void onRequestSigning2( + String id, String algorithm, String host, int port, byte[] data, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postRequestSigning(id, algorithm, host, port, data); + } + } + + @Override public void onRequestCertificate(String host, int port, int seq) { synchronized (mSessionCallbackRecordMap) { SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); @@ -2258,6 +2271,17 @@ public final class TvInteractiveAppManager { }); } + void postRequestSigning(String id, String algorithm, String host, int port, + byte[] data) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onRequestSigning(mSession, id, algorithm, host, + port, data); + } + }); + } + void postRequestCertificate(String host, int port) { mHandler.post(new Runnable() { @Override @@ -2609,6 +2633,25 @@ public final class TvInteractiveAppManager { } /** + * This is called when + * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, int, byte[])} is + * called. + * + * @param session A {@link TvInteractiveAppService.Session} associated with this callback. + * @param signingId the ID to identify the request. + * @param algorithm the standard name of the signature algorithm requested, such as + * MD5withRSA, SHA256withDSA, etc. + * @param host The host of the SSL CLient Authentication Server + * @param port The port of the SSL Client Authentication Server + * @param data the original bytes to be signed. + * @hide + */ + public void onRequestSigning( + Session session, String signingId, String algorithm, String host, + int port, byte[] data) { + } + + /** * This is called when the service requests a SSL certificate for client validation. * * @param session A {@link TvInteractiveAppService.Session} associated with this callback. diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java index 054b272d820f..7b6dc38fe22c 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java @@ -1645,6 +1645,50 @@ public abstract class TvInteractiveAppService extends Service { } /** + * Requests signing of the given data. + * + * <p>This is used when the corresponding server of the broadcast-independent interactive + * app requires signing during handshaking, and the interactive app service doesn't have + * the built-in private key. The private key is provided by the content providers and + * pre-built in the related app, such as TV app. + * + * @param signingId the ID to identify the request. When a result is received, this ID can + * be used to correlate the result with the request. + * @param algorithm the standard name of the signature algorithm requested, such as + * MD5withRSA, SHA256withDSA, etc. The name is from standards like + * FIPS PUB 186-4 and PKCS #1. + * @param host the host of the SSL client authentication server. + * @param port the port of the SSL client authentication server. + * @param data the original bytes to be signed. + * + * @see #onSigningResult(String, byte[]) + * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) + * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS + * @hide + */ + @CallSuper + public void requestSigning(@NonNull String signingId, @NonNull String algorithm, + @NonNull String host, int port, @NonNull byte[] data) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "requestSigning"); + } + if (mSessionCallback != null) { + mSessionCallback.onRequestSigning2(signingId, algorithm, + host, port, data); + } + } catch (RemoteException e) { + Log.w(TAG, "error in requestSigning", e); + } + } + }); + } + + /** * Requests a SSL certificate for client validation. * * @param host the host name of the SSL authentication server. diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index ef90bf993437..8cdd59e51ffe 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -163,6 +163,13 @@ static struct { static struct { jclass clazz; jmethodID ctorId; + jmethodID sizeId; + jmethodID addId; +} gArrayDequeInfo; + +static struct { + jclass clazz; + jmethodID ctorId; jmethodID setInternalStateId; jfieldID contextId; jfieldID validId; @@ -200,8 +207,14 @@ struct fields_t { jfieldID queueRequestIndexID; jfieldID outputFrameLinearBlockID; jfieldID outputFrameHardwareBufferID; + jfieldID outputFramebufferInfosID; jfieldID outputFrameChangedKeysID; jfieldID outputFrameFormatID; + jfieldID bufferInfoFlags; + jfieldID bufferInfoOffset; + jfieldID bufferInfoSize; + jfieldID bufferInfoPresentationTimeUs; + }; static fields_t gFields; @@ -412,6 +425,22 @@ status_t JMediaCodec::queueInputBuffer( index, offset, size, timeUs, flags, errorDetailMsg); } +status_t JMediaCodec::queueInputBuffers( + size_t index, + size_t offset, + size_t size, + const sp<RefBase> &infos, + AString *errorDetailMsg) { + + sp<BufferInfosWrapper> auInfo((BufferInfosWrapper *)infos.get()); + return mCodec->queueInputBuffers( + index, + offset, + size, + auInfo, + errorDetailMsg); +} + status_t JMediaCodec::queueSecureInputBuffer( size_t index, size_t offset, @@ -430,10 +459,11 @@ status_t JMediaCodec::queueSecureInputBuffer( } status_t JMediaCodec::queueBuffer( - size_t index, const std::shared_ptr<C2Buffer> &buffer, int64_t timeUs, - uint32_t flags, const sp<AMessage> &tunings, AString *errorDetailMsg) { + size_t index, const std::shared_ptr<C2Buffer> &buffer, + const sp<RefBase> &infos, const sp<AMessage> &tunings, AString *errorDetailMsg) { + sp<BufferInfosWrapper> auInfo((BufferInfosWrapper *)infos.get()); return mCodec->queueBuffer( - index, buffer, timeUs, flags, tunings, errorDetailMsg); + index, buffer, auInfo, tunings, errorDetailMsg); } status_t JMediaCodec::queueEncryptedLinearBlock( @@ -446,13 +476,13 @@ status_t JMediaCodec::queueEncryptedLinearBlock( const uint8_t iv[16], CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern, - int64_t presentationTimeUs, - uint32_t flags, + const sp<RefBase> &infos, const sp<AMessage> &tunings, AString *errorDetailMsg) { + sp<BufferInfosWrapper> auInfo((BufferInfosWrapper *)infos.get()); return mCodec->queueEncryptedBuffer( index, buffer, offset, subSamples, numSubSamples, key, iv, mode, pattern, - presentationTimeUs, flags, tunings, errorDetailMsg); + auInfo, tunings, errorDetailMsg); } status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { @@ -722,6 +752,42 @@ status_t JMediaCodec::getImage( return OK; } +void maybeSetBufferInfos(JNIEnv *env, jobject &frame, const sp<BufferInfosWrapper> &bufInfos) { + if (!bufInfos) { + return; + } + std::vector<AccessUnitInfo> &infos = bufInfos.get()->value; + if (infos.empty()) { + return; + } + ScopedLocalRef<jobject> dequeObj{env, env->NewObject( + gArrayDequeInfo.clazz, gArrayDequeInfo.ctorId)}; + jint offset = 0; + std::vector<jobject> jObjectInfos; + for (int i = 0 ; i < infos.size(); i++) { + jobject bufferInfo = env->NewObject( + gBufferInfo.clazz, gBufferInfo.ctorId); + if (bufferInfo != NULL) { + env->CallVoidMethod(bufferInfo, gBufferInfo.setId, + offset, + (jint)(infos)[i].mSize, + (infos)[i].mTimestamp, + (infos)[i].mFlags); + (void)env->CallBooleanMethod( + dequeObj.get(), gArrayDequeInfo.addId, bufferInfo); + offset += (infos)[i].mSize; + jObjectInfos.push_back(bufferInfo); + } + } + env->SetObjectField( + frame, + gFields.outputFramebufferInfosID, + dequeObj.get()); + for (int i = 0; i < jObjectInfos.size(); i++) { + env->DeleteLocalRef(jObjectInfos[i]); + } +} + status_t JMediaCodec::getOutputFrame( JNIEnv *env, jobject frame, size_t index) const { sp<MediaCodecBuffer> buffer; @@ -732,6 +798,11 @@ status_t JMediaCodec::getOutputFrame( } if (buffer->size() > 0) { + sp<RefBase> obj; + sp<BufferInfosWrapper> bufInfos; + if (buffer->meta()->findObject("accessUnitInfo", &obj)) { + bufInfos = std::move(((decltype(bufInfos.get()))obj.get())); + } std::shared_ptr<C2Buffer> c2Buffer = buffer->asC2Buffer(); if (c2Buffer) { switch (c2Buffer->data().type()) { @@ -747,6 +818,7 @@ status_t JMediaCodec::getOutputFrame( (jlong)context.release(), true); env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get()); + maybeSetBufferInfos(env, frame, bufInfos); break; } case C2BufferData::GRAPHIC: { @@ -787,6 +859,7 @@ status_t JMediaCodec::getOutputFrame( (jlong)context.release(), true); env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get()); + maybeSetBufferInfos(env, frame, bufInfos); } else { // No-op. } @@ -1250,6 +1323,7 @@ static jthrowable createCryptoException(JNIEnv *env, status_t err, void JMediaCodec::handleCallback(const sp<AMessage> &msg) { int32_t arg1, arg2 = 0; jobject obj = NULL; + std::vector<jobject> jObjectInfos; CHECK(msg->findInt32("callbackID", &arg1)); JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -1287,6 +1361,35 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { break; } + case MediaCodec::CB_LARGE_FRAME_OUTPUT_AVAILABLE: + { + sp<RefBase> spobj = nullptr; + CHECK(msg->findInt32("index", &arg2)); + CHECK(msg->findObject("accessUnitInfo", &spobj)); + if (spobj != nullptr) { + sp<BufferInfosWrapper> bufferInfoParamsWrapper { + (BufferInfosWrapper *)spobj.get()}; + std::vector<AccessUnitInfo> &bufferInfoParams = + bufferInfoParamsWrapper.get()->value; + obj = env->NewObject(gArrayDequeInfo.clazz, gArrayDequeInfo.ctorId); + jint offset = 0; + for (int i = 0 ; i < bufferInfoParams.size(); i++) { + jobject bufferInfo = env->NewObject(gBufferInfo.clazz, gBufferInfo.ctorId); + if (bufferInfo != NULL) { + env->CallVoidMethod(bufferInfo, gBufferInfo.setId, + offset, + (jint)(bufferInfoParams)[i].mSize, + (bufferInfoParams)[i].mTimestamp, + (bufferInfoParams)[i].mFlags); + (void)env->CallBooleanMethod(obj, gArrayDequeInfo.addId, bufferInfo); + offset += (bufferInfoParams)[i].mSize; + jObjectInfos.push_back(bufferInfo); + } + } + } + break; + } + case MediaCodec::CB_CRYPTO_ERROR: { int32_t err, actionCode; @@ -1346,6 +1449,9 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { arg2, obj); + for (int i = 0; i < jObjectInfos.size(); i++) { + env->DeleteLocalRef(jObjectInfos[i]); + } env->DeleteLocalRef(obj); } @@ -1913,6 +2019,103 @@ static void android_media_MediaCodec_queueInputBuffer( codec->getExceptionMessage(errorDetailMsg.c_str()).c_str()); } +static status_t extractInfosFromObject( + JNIEnv * const env, + jint * const initialOffset, + jint * const totalSize, + std::vector<AccessUnitInfo> * const infos, + const jobjectArray &objArray, + AString * const errorDetailMsg) { + if (totalSize == nullptr + || initialOffset == nullptr + || infos == nullptr) { + if (errorDetailMsg) { + *errorDetailMsg = "Error: Null arguments provided for extracting Access unit info"; + } + return BAD_VALUE; + } + const jsize numEntries = env->GetArrayLength(objArray); + if (numEntries <= 0) { + if (errorDetailMsg) { + *errorDetailMsg = "Error: No BufferInfo found while queuing for large frame input"; + } + return BAD_VALUE; + } + *initialOffset = 0; + *totalSize = 0; + for (jsize i = 0; i < numEntries; i++) { + jobject param = env->GetObjectArrayElement(objArray, i); + if (param == NULL) { + if (errorDetailMsg) { + *errorDetailMsg = "Error: Queuing a null BufferInfo"; + } + return BAD_VALUE; + } + size_t offset = static_cast<size_t>(env->GetIntField(param, gFields.bufferInfoOffset)); + size_t size = static_cast<size_t>(env->GetIntField(param, gFields.bufferInfoSize)); + uint32_t flags = static_cast<uint32_t>(env->GetIntField(param, gFields.bufferInfoFlags)); + if (flags == 0 && size == 0) { + if (errorDetailMsg) { + *errorDetailMsg = "Error: Queuing an empty BufferInfo"; + } + return BAD_VALUE; + } + if (i == 0) { + *initialOffset = offset; + } + if (CC_UNLIKELY((offset > UINT32_MAX) + || ((long)(offset + size) > UINT32_MAX) + || ((offset - *initialOffset) != *totalSize))) { + if (errorDetailMsg) { + *errorDetailMsg = "Error: offset/size in BufferInfo"; + } + return BAD_VALUE; + } + infos->emplace_back( + flags, + size, + env->GetLongField(param, gFields.bufferInfoPresentationTimeUs)); + *totalSize += size; + } + return OK; +} + +static void android_media_MediaCodec_queueInputBuffers( + JNIEnv *env, + jobject thiz, + jint index, + jobjectArray objArray) { + ALOGV("android_media_MediaCodec_queueInputBuffers"); + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + if (codec == NULL || codec->initCheck() != OK || objArray == NULL) { + throwExceptionAsNecessary(env, INVALID_OPERATION, codec); + return; + } + sp<BufferInfosWrapper> infoObj = + new BufferInfosWrapper{decltype(infoObj->value)()}; + AString errorDetailMsg; + jint initialOffset = 0; + jint totalSize = 0; + status_t err = extractInfosFromObject( + env, + &initialOffset, + &totalSize, + &infoObj->value, + objArray, + &errorDetailMsg); + if (err == OK) { + err = codec->queueInputBuffers( + index, + initialOffset, + totalSize, + infoObj, + &errorDetailMsg); + } + throwExceptionAsNecessary( + env, err, ACTION_CODE_FATAL, + codec->getExceptionMessage(errorDetailMsg.c_str()).c_str()); +} + struct NativeCryptoInfo { NativeCryptoInfo(JNIEnv *env, jobject cryptoInfoObj) : mEnv{env}, @@ -2559,8 +2762,7 @@ static void extractBufferFromContext( static void android_media_MediaCodec_native_queueLinearBlock( JNIEnv *env, jobject thiz, jint index, jobject bufferObj, - jint offset, jint size, jobject cryptoInfoObj, - jlong presentationTimeUs, jint flags, jobject keys, jobject values) { + jobject cryptoInfoObj, jobjectArray objArray, jobject keys, jobject values) { ALOGV("android_media_MediaCodec_native_queueLinearBlock"); sp<JMediaCodec> codec = getMediaCodec(env, thiz); @@ -2578,7 +2780,24 @@ static void android_media_MediaCodec_native_queueLinearBlock( "error occurred while converting tunings from Java to native"); return; } - + jint totalSize; + jint initialOffset; + std::vector<AccessUnitInfo> infoVec; + AString errorDetailMsg; + err = extractInfosFromObject(env, + &initialOffset, + &totalSize, + &infoVec, + objArray, + &errorDetailMsg); + if (err != OK) { + throwExceptionAsNecessary( + env, INVALID_OPERATION, ACTION_CODE_FATAL, + codec->getExceptionMessage(errorDetailMsg.c_str()).c_str()); + return; + } + sp<BufferInfosWrapper> infos = + new BufferInfosWrapper{std::move(infoVec)}; std::shared_ptr<C2Buffer> buffer; sp<hardware::HidlMemory> memory; ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gLinearBlockInfo.lockId)}; @@ -2587,10 +2806,10 @@ static void android_media_MediaCodec_native_queueLinearBlock( JMediaCodecLinearBlock *context = (JMediaCodecLinearBlock *)env->GetLongField(bufferObj, gLinearBlockInfo.contextId); if (codec->hasCryptoOrDescrambler()) { - extractMemoryFromContext(context, offset, size, &memory); - offset += context->mHidlMemoryOffset; + extractMemoryFromContext(context, initialOffset, totalSize, &memory); + initialOffset += context->mHidlMemoryOffset; } else { - extractBufferFromContext(context, offset, size, &buffer); + extractBufferFromContext(context, initialOffset, totalSize, &buffer); } } env->MonitorExit(lock.get()); @@ -2601,7 +2820,6 @@ static void android_media_MediaCodec_native_queueLinearBlock( return; } - AString errorDetailMsg; if (codec->hasCryptoOrDescrambler()) { if (!memory) { // It means there was an unexpected failure in extractMemoryFromContext above @@ -2615,7 +2833,7 @@ static void android_media_MediaCodec_native_queueLinearBlock( return; } auto cryptoInfo = - cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{size}; + cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{totalSize}; if (env->ExceptionCheck()) { // Creation of cryptoInfo failed. Let the exception bubble up. return; @@ -2623,13 +2841,12 @@ static void android_media_MediaCodec_native_queueLinearBlock( err = codec->queueEncryptedLinearBlock( index, memory, - offset, + initialOffset, cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples, (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv, cryptoInfo.mMode, cryptoInfo.mPattern, - presentationTimeUs, - flags, + infos, tunings, &errorDetailMsg); ALOGI_IF(err != OK, "queueEncryptedLinearBlock returned err = %d", err); @@ -2646,7 +2863,7 @@ static void android_media_MediaCodec_native_queueLinearBlock( return; } err = codec->queueBuffer( - index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg); + index, buffer, infos, tunings, &errorDetailMsg); } throwExceptionAsNecessary( env, err, ACTION_CODE_FATAL, @@ -2704,8 +2921,11 @@ static void android_media_MediaCodec_native_queueHardwareBuffer( std::shared_ptr<C2Buffer> buffer = C2Buffer::CreateGraphicBuffer(block->share( block->crop(), C2Fence{})); AString errorDetailMsg; + sp<BufferInfosWrapper> infos = + new BufferInfosWrapper{decltype(infos->value)()}; + infos->value.emplace_back(flags, 0 /*not used*/, presentationTimeUs); err = codec->queueBuffer( - index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg); + index, buffer, infos, tunings, &errorDetailMsg); throwExceptionAsNecessary( env, err, ACTION_CODE_FATAL, codec->getExceptionMessage(errorDetailMsg.c_str()).c_str()); @@ -3214,6 +3434,10 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { env->GetFieldID(clazz.get(), "mLinearBlock", "Landroid/media/MediaCodec$LinearBlock;"); CHECK(gFields.outputFrameLinearBlockID != NULL); + gFields.outputFramebufferInfosID = + env->GetFieldID(clazz.get(), "mBufferInfos", "Ljava/util/ArrayDeque;"); + CHECK(gFields.outputFramebufferInfosID != NULL); + gFields.outputFrameHardwareBufferID = env->GetFieldID(clazz.get(), "mHardwareBuffer", "Landroid/hardware/HardwareBuffer;"); CHECK(gFields.outputFrameHardwareBufferID != NULL); @@ -3401,6 +3625,19 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { gArrayListInfo.addId = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z"); CHECK(gArrayListInfo.addId != NULL); + clazz.reset(env->FindClass("java/util/ArrayDeque")); + CHECK(clazz.get() != NULL); + gArrayDequeInfo.clazz = (jclass)env->NewGlobalRef(clazz.get()); + + gArrayDequeInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V"); + CHECK(gArrayDequeInfo.ctorId != NULL); + + gArrayDequeInfo.sizeId = env->GetMethodID(clazz.get(), "size", "()I"); + CHECK(gArrayDequeInfo.sizeId != NULL); + + gArrayDequeInfo.addId = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z"); + CHECK(gArrayDequeInfo.addId != NULL); + clazz.reset(env->FindClass("android/media/MediaCodec$LinearBlock")); CHECK(clazz.get() != NULL); @@ -3444,6 +3681,12 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { gBufferInfo.setId = env->GetMethodID(clazz.get(), "set", "(IIJI)V"); CHECK(gBufferInfo.setId != NULL); + + gFields.bufferInfoSize = env->GetFieldID(clazz.get(), "size", "I"); + gFields.bufferInfoFlags = env->GetFieldID(clazz.get(), "flags", "I"); + gFields.bufferInfoOffset = env->GetFieldID(clazz.get(), "offset", "I"); + gFields.bufferInfoPresentationTimeUs = + env->GetFieldID(clazz.get(), "presentationTimeUs", "J"); } static void android_media_MediaCodec_native_setup( @@ -3701,6 +3944,9 @@ static const JNINativeMethod gMethods[] = { { "native_queueInputBuffer", "(IIIJI)V", (void *)android_media_MediaCodec_queueInputBuffer }, + { "native_queueInputBuffers", "(I[Ljava/lang/Object;)V", + (void *)android_media_MediaCodec_queueInputBuffers }, + { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V", (void *)android_media_MediaCodec_queueSecureInputBuffer }, @@ -3711,8 +3957,8 @@ static const JNINativeMethod gMethods[] = { { "native_closeMediaImage", "(J)V", (void *)android_media_MediaCodec_closeMediaImage }, { "native_queueLinearBlock", - "(ILandroid/media/MediaCodec$LinearBlock;IILandroid/media/MediaCodec$CryptoInfo;JI" - "Ljava/util/ArrayList;Ljava/util/ArrayList;)V", + "(ILandroid/media/MediaCodec$LinearBlock;Landroid/media/MediaCodec$CryptoInfo;" + "[Ljava/lang/Object;Ljava/util/ArrayList;Ljava/util/ArrayList;)V", (void *)android_media_MediaCodec_native_queueLinearBlock }, { "native_queueHardwareBuffer", diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index fbaf64fda572..02708efdea3a 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -35,6 +35,7 @@ class C2Buffer; namespace android { struct ABuffer; +struct AccessUnitInfo; struct ALooper; struct AMessage; struct AString; @@ -93,6 +94,13 @@ struct JMediaCodec : public AHandler { size_t offset, size_t size, int64_t timeUs, uint32_t flags, AString *errorDetailMsg); + status_t queueInputBuffers( + size_t index, + size_t offset, + size_t size, + const sp<RefBase> &auInfo, + AString *errorDetailMsg = NULL); + status_t queueSecureInputBuffer( size_t index, size_t offset, @@ -108,7 +116,7 @@ struct JMediaCodec : public AHandler { status_t queueBuffer( size_t index, const std::shared_ptr<C2Buffer> &buffer, - int64_t timeUs, uint32_t flags, const sp<AMessage> &tunings, + const sp<RefBase> &infos, const sp<AMessage> &tunings, AString *errorDetailMsg); status_t queueEncryptedLinearBlock( @@ -121,8 +129,7 @@ struct JMediaCodec : public AHandler { const uint8_t iv[16], CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern, - int64_t presentationTimeUs, - uint32_t flags, + const sp<RefBase> &infos, const sp<AMessage> &tunings, AString *errorDetailMsg); diff --git a/media/jni/soundpool/SoundDecoder.cpp b/media/jni/soundpool/SoundDecoder.cpp index 5ed10b0d785f..ae576342c6cc 100644 --- a/media/jni/soundpool/SoundDecoder.cpp +++ b/media/jni/soundpool/SoundDecoder.cpp @@ -29,14 +29,15 @@ static constexpr size_t kMaxQueueSize = 128; // before the SoundDecoder thread closes. static constexpr int32_t kWaitTimeBeforeCloseMs = 1000; -SoundDecoder::SoundDecoder(SoundManager* soundManager, size_t threads) +SoundDecoder::SoundDecoder(SoundManager* soundManager, size_t threads, int32_t threadPriority) : mSoundManager(soundManager) { ALOGV("%s(%p, %zu)", __func__, soundManager, threads); // ThreadPool is created, but we don't launch any threads. mThreadPool = std::make_unique<ThreadPool>( std::min(threads, (size_t)std::thread::hardware_concurrency()), - "SoundDecoder_"); + "SoundDecoder_", + threadPriority); } SoundDecoder::~SoundDecoder() diff --git a/media/jni/soundpool/SoundDecoder.h b/media/jni/soundpool/SoundDecoder.h index 7b62114483cf..3f44a0d977e1 100644 --- a/media/jni/soundpool/SoundDecoder.h +++ b/media/jni/soundpool/SoundDecoder.h @@ -28,7 +28,7 @@ namespace android::soundpool { */ class SoundDecoder { public: - SoundDecoder(SoundManager* soundManager, size_t threads); + SoundDecoder(SoundManager* soundManager, size_t threads, int32_t threadPriority); ~SoundDecoder(); void loadSound(int32_t soundID) NO_THREAD_SAFETY_ANALYSIS; // uses unique_lock void quit(); diff --git a/media/jni/soundpool/SoundManager.cpp b/media/jni/soundpool/SoundManager.cpp index 5b16174eef2b..fa35813391a6 100644 --- a/media/jni/soundpool/SoundManager.cpp +++ b/media/jni/soundpool/SoundManager.cpp @@ -29,7 +29,7 @@ namespace android::soundpool { static const size_t kDecoderThreads = std::thread::hardware_concurrency() >= 4 ? 2 : 1; SoundManager::SoundManager() - : mDecoder{std::make_unique<SoundDecoder>(this, kDecoderThreads)} + : mDecoder{std::make_unique<SoundDecoder>(this, kDecoderThreads, ANDROID_PRIORITY_NORMAL)} { ALOGV("%s()", __func__); } diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 66fec1c528e7..e11ccbc0448b 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -126,7 +126,8 @@ StreamManager::StreamManager( mThreadPool = std::make_unique<ThreadPool>( std::min((size_t)streams, // do not make more threads than streams to play std::min(threads, (size_t)std::thread::hardware_concurrency())), - "SoundPool_"); + "SoundPool_", + ANDROID_PRIORITY_AUDIO); } #pragma clang diagnostic pop diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h index 340b49bc6d6c..a4cb28663bda 100644 --- a/media/jni/soundpool/StreamManager.h +++ b/media/jni/soundpool/StreamManager.h @@ -46,9 +46,9 @@ namespace android::soundpool { */ class JavaThread { public: - JavaThread(std::function<void()> f, const char *name) + JavaThread(std::function<void()> f, const char *name, int32_t threadPriority) : mF{std::move(f)} { - createThreadEtc(staticFunction, this, name, ANDROID_PRIORITY_AUDIO); + createThreadEtc(staticFunction, this, name, threadPriority); } JavaThread(JavaThread &&) = delete; // uses "this" ptr, not moveable. @@ -109,9 +109,11 @@ private: */ class ThreadPool { public: - ThreadPool(size_t maxThreadCount, std::string name) + ThreadPool(size_t maxThreadCount, std::string name, + int32_t threadPriority = ANDROID_PRIORITY_NORMAL) : mMaxThreadCount(maxThreadCount) - , mName{std::move(name)} { } + , mName{std::move(name)} + , mThreadPriority(threadPriority) {} ~ThreadPool() { quit(); } @@ -159,7 +161,8 @@ public: const int32_t id = mNextThreadId; mThreads.emplace_back(std::make_unique<JavaThread>( [this, id, mf = std::move(f)] { mf(id); --mActiveThreadCount; }, - (mName + std::to_string(id)).c_str())); + (mName + std::to_string(id)).c_str(), + mThreadPriority)); ++mActiveThreadCount; return id; } @@ -180,6 +183,7 @@ public: private: const size_t mMaxThreadCount; const std::string mName; + const int32_t mThreadPriority; std::atomic_size_t mActiveThreadCount = 0; diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java index 774de5fbbe3e..0df36afddf25 100644 --- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java +++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java @@ -19,7 +19,7 @@ package android.media.projection; import static android.Manifest.permission.MANAGE_MEDIA_PROJECTION; import android.annotation.EnforcePermission; -import android.os.IBinder; +import android.app.ActivityOptions.LaunchCookie; import android.os.PermissionEnforcer; import android.os.RemoteException; @@ -29,7 +29,7 @@ import android.os.RemoteException; */ public final class FakeIMediaProjection extends IMediaProjection.Stub { boolean mIsStarted = false; - IBinder mLaunchCookie = null; + LaunchCookie mLaunchCookie = null; IMediaProjectionCallback mIMediaProjectionCallback = null; FakeIMediaProjection(PermissionEnforcer enforcer) { @@ -80,14 +80,14 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { @Override @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public IBinder getLaunchCookie() throws RemoteException { + public LaunchCookie getLaunchCookie() throws RemoteException { getLaunchCookie_enforcePermission(); return mLaunchCookie; } @Override @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void setLaunchCookie(IBinder launchCookie) throws RemoteException { + public void setLaunchCookie(LaunchCookie launchCookie) throws RemoteException { setLaunchCookie_enforcePermission(); mLaunchCookie = launchCookie; } diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java index 2e0396fc6119..1323e89202ee 100644 --- a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java +++ b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java @@ -31,15 +31,14 @@ import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import android.annotation.Nullable; +import android.app.ActivityOptions.LaunchCookie; import android.compat.testing.PlatformCompatChangeRule; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; @@ -117,7 +116,7 @@ public class MediaProjectionTest { permissionEnforcer.grant(MANAGE_MEDIA_PROJECTION); // Support the MediaProjection instance. mFakeIMediaProjection = new FakeIMediaProjection(permissionEnforcer); - mFakeIMediaProjection.setLaunchCookie(mock(IBinder.class)); + mFakeIMediaProjection.setLaunchCookie(new LaunchCookie()); mMediaProjection = new MediaProjection(mTestableContext, mFakeIMediaProjection, mDisplayManager); diff --git a/nfc/jarjar-rules.txt b/nfc/jarjar-rules.txt index 4cd652d6af7f..99ae14414dad 100644 --- a/nfc/jarjar-rules.txt +++ b/nfc/jarjar-rules.txt @@ -4,6 +4,7 @@ rule android.content.ComponentNameProto* com.android.nfc.x.@0 rule android.content.IntentProto* com.android.nfc.x.@0 rule android.content.IntentFilterProto* com.android.nfc.x.@0 rule android.content.AuthorityEntryProto* com.android.nfc.x.@0 +rule android.content.UriRelativeFilter* com.android.nfc.x.@0 rule android.nfc.cardemulation.AidGroupProto* com.android.nfc.x.@0 rule android.nfc.cardemulation.ApduServiceInfoProto* com.android.nfc.x.@0 rule android.nfc.cardemulation.NfcFServiceInfoProto* com.android.nfc.x.@0 diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index 9d38e4c5b297..1f41b812164c 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -338,8 +338,10 @@ public final class CardEmulation { } } /** - * Sets whether the system should default to observe mode or not when - * the service is in the foreground or the default payment service. + * Sets whether the system should default to observe mode or not when the service is in the + * foreground or the default payment service. The default is to not enable observe mode when + * a service either the foreground default service or the default payment service so not + * calling this method will preserve that behavior. * * @param service The component name of the service * @param enable Whether the servic should default to observe mode or not diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig new file mode 100644 index 000000000000..563626634068 --- /dev/null +++ b/packages/CrashRecovery/aconfig/flags.aconfig @@ -0,0 +1,9 @@ +package: "android.crashrecovery.flags" + +flag { + name: "recoverability_detection" + namespace: "package_watchdog" + description: "Feature flag for recoverability detection" + bug: "310236690" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml index a2d2a96fe591..3614240a5f7f 100644 --- a/packages/CredentialManager/res/values-af/strings.xml +++ b/packages/CredentialManager/res/values-af/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Van ’n ander toestel af"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gebruik ’n ander toestel"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Versoek is deur <xliff:g id="APP_NAME">%1$s</xliff:g> gekanselleer"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml index 475dcf796109..e5759fa2c558 100644 --- a/packages/CredentialManager/res/values-am/strings.xml +++ b/packages/CredentialManager/res/values-am/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ከሌላ መሣሪያ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"የተለየ መሣሪያ ይጠቀሙ"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"ጥያቄ በ<xliff:g id="APP_NAME">%1$s</xliff:g> ተሰርዟል"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml index 3f85b5827b76..fdd9043a5708 100644 --- a/packages/CredentialManager/res/values-ar/strings.xml +++ b/packages/CredentialManager/res/values-ar/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"من جهاز آخر"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استخدام جهاز مختلف"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"تم إلغاء الطلب بواسطة \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-as/strings.xml b/packages/CredentialManager/res/values-as/strings.xml index e14b34b83060..005079e7d68d 100644 --- a/packages/CredentialManager/res/values-as/strings.xml +++ b/packages/CredentialManager/res/values-as/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"অন্য এটা ডিভাইচৰ পৰা"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"অন্য এটা ডিভাইচ ব্যৱহাৰ কৰক"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ অনুৰোধটো বাতিল কৰিছে"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-az/strings.xml b/packages/CredentialManager/res/values-az/strings.xml index d0f8bb0ba1c2..9937942646f3 100644 --- a/packages/CredentialManager/res/values-az/strings.xml +++ b/packages/CredentialManager/res/values-az/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Başqa cihazdan"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Başqa cihaz istifadə edin"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> sorğunu ləğv etdi"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml index 780274cc0a5b..171e8411e4f0 100644 --- a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Sa drugog uređaja"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Koristi drugi uređaj"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Zahtve je otkazala aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml index 144bb8661472..323b6831117b 100644 --- a/packages/CredentialManager/res/values-be/strings.xml +++ b/packages/CredentialManager/res/values-be/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншай прылады"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Скарыстаць іншую прыладу"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Запыт скасаваны праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml index ef4dd54c59f1..1efec456384c 100644 --- a/packages/CredentialManager/res/values-bg/strings.xml +++ b/packages/CredentialManager/res/values-bg/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"От друго устройство"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Използване на друго устройство"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Заявката е анулирана от <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml index 14f4a9baacd6..3fcc91a08eab 100644 --- a/packages/CredentialManager/res/values-bn/strings.xml +++ b/packages/CredentialManager/res/values-bn/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"অন্য ডিভাইস থেকে"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"আলাদা ডিভাইস ব্যবহার করুন"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> দ্বারা অনুরোধ বাতিল করা হয়েছে"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml index 6315ea83c237..6a3d25e87102 100644 --- a/packages/CredentialManager/res/values-bs/strings.xml +++ b/packages/CredentialManager/res/values-bs/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"S drugog uređaja"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je otkazala zahtjev"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml index 8e3fda8ebf31..d491b7067e08 100644 --- a/packages/CredentialManager/res/values-ca/strings.xml +++ b/packages/CredentialManager/res/values-ca/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Des d\'un altre dispositiu"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utilitza un dispositiu diferent"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha cancel·lat la sol·licitud"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml index 9fe5a492c8e7..133f3f8058a1 100644 --- a/packages/CredentialManager/res/values-cs/strings.xml +++ b/packages/CredentialManager/res/values-cs/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z jiného zařízení"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použít jiné zařízení"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádost zrušila"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml index c7ee0380a7f6..93de75fb6c66 100644 --- a/packages/CredentialManager/res/values-da/strings.xml +++ b/packages/CredentialManager/res/values-da/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en anden enhed"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Brug en anden enhed"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Anmodningen blev annulleret af <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml index 29d9a86b5512..d3648a17cb20 100644 --- a/packages/CredentialManager/res/values-de/strings.xml +++ b/packages/CredentialManager/res/values-de/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Von einem anderen Gerät"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Anderes Gerät verwenden"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Anfrage abgebrochen von <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml index 089f8989a30f..da3ae138c1bc 100644 --- a/packages/CredentialManager/res/values-el/strings.xml +++ b/packages/CredentialManager/res/values-el/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Από άλλη συσκευή"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Χρήση διαφορετικής συσκευής"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Το αίτημα ακυρώθηκε από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml index 7b80db05da7c..056b8ff6f20f 100644 --- a/packages/CredentialManager/res/values-en-rAU/strings.xml +++ b/packages/CredentialManager/res/values-en-rAU/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Request cancelled by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-en-rCA/strings.xml b/packages/CredentialManager/res/values-en-rCA/strings.xml index b17361635df9..e23711f6bfb8 100644 --- a/packages/CredentialManager/res/values-en-rCA/strings.xml +++ b/packages/CredentialManager/res/values-en-rCA/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Request cancelled by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml index 7b80db05da7c..056b8ff6f20f 100644 --- a/packages/CredentialManager/res/values-en-rGB/strings.xml +++ b/packages/CredentialManager/res/values-en-rGB/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Request cancelled by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml index 7b80db05da7c..056b8ff6f20f 100644 --- a/packages/CredentialManager/res/values-en-rIN/strings.xml +++ b/packages/CredentialManager/res/values-en-rIN/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Request cancelled by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-en-rXC/strings.xml b/packages/CredentialManager/res/values-en-rXC/strings.xml index c3eeb04148e4..0db7324562ce 100644 --- a/packages/CredentialManager/res/values-en-rXC/strings.xml +++ b/packages/CredentialManager/res/values-en-rXC/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Request cancelled by <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml index f6a5dcbc839f..818d71508307 100644 --- a/packages/CredentialManager/res/values-es-rUS/strings.xml +++ b/packages/CredentialManager/res/values-es-rUS/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Desde otro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otra voz"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> canceló la solicitud"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml index fb0cbf985625..f9776fb05227 100644 --- a/packages/CredentialManager/res/values-es/strings.xml +++ b/packages/CredentialManager/res/values-es/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De otro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otro dispositivo"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha cancelado la solicitud"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml index 97dbe4d19d9e..4870e70ad870 100644 --- a/packages/CredentialManager/res/values-et/strings.xml +++ b/packages/CredentialManager/res/values-et/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Muus seadmes"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Kasuta teist seadet"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> tühistas taotluse"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml index 8316283a83ff..21a66f9ceda0 100644 --- a/packages/CredentialManager/res/values-eu/strings.xml +++ b/packages/CredentialManager/res/values-eu/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Beste gailu batean gordetakoak"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Erabili beste gailu bat"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Utzi du bertan behera eskaera <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml index a6e0d3d11eee..47385cd1d086 100644 --- a/packages/CredentialManager/res/values-fa/strings.xml +++ b/packages/CredentialManager/res/values-fa/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"از دستگاهی دیگر"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استفاده از دستگاه دیگری"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"درخواست را <xliff:g id="APP_NAME">%1$s</xliff:g> لغو کرد"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml index fff45c9df29d..d463ea168993 100644 --- a/packages/CredentialManager/res/values-fi/strings.xml +++ b/packages/CredentialManager/res/values-fi/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Toiselta laitteelta"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Käytä toista laitetta"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> hylkäsi pyynnön"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml index 155be6fc90db..35966044ef89 100644 --- a/packages/CredentialManager/res/values-fr-rCA/strings.xml +++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"À partir d\'un autre appareil"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utiliser un autre appareil"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Demande annulée par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml index e042815a0452..fe545b3807a7 100644 --- a/packages/CredentialManager/res/values-fr/strings.xml +++ b/packages/CredentialManager/res/values-fr/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Depuis un autre appareil"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utiliser un autre appareil"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Requête annulée par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml index 79e0b5e97f60..6341ed89c82a 100644 --- a/packages/CredentialManager/res/values-gl/strings.xml +++ b/packages/CredentialManager/res/values-gl/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Doutro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar outro dispositivo"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> cancelou a solicitude"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-gu/strings.xml b/packages/CredentialManager/res/values-gu/strings.xml index c3c5b629a83b..19930dfaba00 100644 --- a/packages/CredentialManager/res/values-gu/strings.xml +++ b/packages/CredentialManager/res/values-gu/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"કોઈ અન્ય ડિવાઇસમાંથી"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"કોઈ અન્ય ડિવાઇસનો ઉપયોગ કરો"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> દ્વારા વિનંતી રદ કરવામાં આવી"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-hi/strings.xml b/packages/CredentialManager/res/values-hi/strings.xml index 05d21e013d27..8505b194b0b3 100644 --- a/packages/CredentialManager/res/values-hi/strings.xml +++ b/packages/CredentialManager/res/values-hi/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"किसी दूसरे डिवाइस से"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"दूसरे डिवाइस का इस्तेमाल करें"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> की ओर से अनुरोध रद्द किया गया"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml index 4425e246398e..3ea884701093 100644 --- a/packages/CredentialManager/res/values-hr/strings.xml +++ b/packages/CredentialManager/res/values-hr/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na drugom uređaju"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Zahtjev je otkazala aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml index c25fa99513d1..620e976dcbab 100644 --- a/packages/CredentialManager/res/values-hu/strings.xml +++ b/packages/CredentialManager/res/values-hu/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Másik eszközről"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Másik eszköz használata"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"A kérelmet törölte a(z) <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml index a6bda50717cd..5423efe463ae 100644 --- a/packages/CredentialManager/res/values-hy/strings.xml +++ b/packages/CredentialManager/res/values-hy/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Մեկ այլ սարքից"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Օգտագործել այլ սարք"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Հարցումը չեղարկվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml index d6bf9463158f..df9d32dcf516 100644 --- a/packages/CredentialManager/res/values-in/strings.xml +++ b/packages/CredentialManager/res/values-in/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Dari perangkat lain"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gunakan perangkat lain"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Permintaan dibatalkan oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-is/strings.xml b/packages/CredentialManager/res/values-is/strings.xml index 76a869f505bf..ba836f9c4fe7 100644 --- a/packages/CredentialManager/res/values-is/strings.xml +++ b/packages/CredentialManager/res/values-is/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Úr öðru tæki"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Nota annað tæki"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> hætti við beiðnina"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml index c5fd89c015f2..d16d11f2f653 100644 --- a/packages/CredentialManager/res/values-it/strings.xml +++ b/packages/CredentialManager/res/values-it/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Da un altro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usa un dispositivo diverso"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Richiesta annullata da <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml index 064356831e3d..a9b01e134b81 100644 --- a/packages/CredentialManager/res/values-iw/strings.xml +++ b/packages/CredentialManager/res/values-iw/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ממכשיר אחר"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"צריך להשתמש במכשיר אחר"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> ביטלה את הבקשה"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml index afbff9042a53..b452ec3ff09a 100644 --- a/packages/CredentialManager/res/values-ja/strings.xml +++ b/packages/CredentialManager/res/values-ja/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"別のデバイスを使う"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"別のデバイスを使用"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> がリクエストをキャンセルしました"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml index 8125ec6b053f..30479335bc86 100644 --- a/packages/CredentialManager/res/values-ka/strings.xml +++ b/packages/CredentialManager/res/values-ka/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"სხვა მოწყობილობიდან"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"გამოიყენეთ სხვა მოწყობილობა"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"თხოვნა გაუქმებულია <xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml index cb68444cc8bf..891a60031e49 100644 --- a/packages/CredentialManager/res/values-kk/strings.xml +++ b/packages/CredentialManager/res/values-kk/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан жасау"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Басқа құрылғыны пайдалану"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы сұрауды тоқтатты."</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-km/strings.xml b/packages/CredentialManager/res/values-km/strings.xml index d361ad9f17ad..d6fc505b4157 100644 --- a/packages/CredentialManager/res/values-km/strings.xml +++ b/packages/CredentialManager/res/values-km/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ពីឧបករណ៍ផ្សេងទៀត"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ប្រើឧបករណ៍ផ្សេង"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"បានបោះបង់សំណើដោយ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml index 7447ab629f72..ff38e197c5b4 100644 --- a/packages/CredentialManager/res/values-kn/strings.xml +++ b/packages/CredentialManager/res/values-kn/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ಮತ್ತೊಂದು ಸಾಧನದಿಂದ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ಬೇರೆ ಸಾಧನವನ್ನು ಬಳಸಿ"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಂದ ವಿನಂತಿಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml index 07a7fbc30946..557c3efc452f 100644 --- a/packages/CredentialManager/res/values-ko/strings.xml +++ b/packages/CredentialManager/res/values-ko/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"다른 기기에서"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"다른 기기 사용"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g>에 의해 요청이 취소됨"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml index e2de2efb60d8..4bc96b1d931a 100644 --- a/packages/CredentialManager/res/values-ky/strings.xml +++ b/packages/CredentialManager/res/values-ky/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Башка түзмөктөн"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Башка түзмөктү колдонуу"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Сурамды <xliff:g id="APP_NAME">%1$s</xliff:g> жокко чыгарды"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml index 3b2e2aa08737..ce103e0c248c 100644 --- a/packages/CredentialManager/res/values-lo/strings.xml +++ b/packages/CredentialManager/res/values-lo/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ຈາກອຸປະກອນອື່ນ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ໃຊ້ອຸປະກອນອື່ນ"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"ການຮ້ອງຂໍຖືກຍົກເລີກໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml index c3b941bfa10e..af278249c97e 100644 --- a/packages/CredentialManager/res/values-lt/strings.xml +++ b/packages/CredentialManager/res/values-lt/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Naudojant kitą įrenginį"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Naudoti kitą įrenginį"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Užklausą atšaukė „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml index 27115caa1802..532ac5eddf3b 100644 --- a/packages/CredentialManager/res/values-lv/strings.xml +++ b/packages/CredentialManager/res/values-lv/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"No citas ierīces"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Izmantot citu ierīci"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> atcēla pieprasījumu"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml index 1f456bfd392f..ec9ebef131dc 100644 --- a/packages/CredentialManager/res/values-mk/strings.xml +++ b/packages/CredentialManager/res/values-mk/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Од друг уред"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Употребете друг уред"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Барањето е откажано од <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ml/strings.xml b/packages/CredentialManager/res/values-ml/strings.xml index 07fea3825a28..16ef8c86bdc0 100644 --- a/packages/CredentialManager/res/values-ml/strings.xml +++ b/packages/CredentialManager/res/values-ml/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"മറ്റൊരു ഉപകരണത്തിൽ നിന്ന്"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"മറ്റൊരു ഉപകരണം ഉപയോഗിക്കുക"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"അഭ്യർത്ഥന <xliff:g id="APP_NAME">%1$s</xliff:g> റദ്ദാക്കി"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml index e37155a72c3a..4a5a8eeafba1 100644 --- a/packages/CredentialManager/res/values-mn/strings.xml +++ b/packages/CredentialManager/res/values-mn/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Өөр төхөөрөмжөөс"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Өөр төхөөрөмж ашиглах"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Хүсэлтийг <xliff:g id="APP_NAME">%1$s</xliff:g> цуцалсан"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml index ceba10185989..6a76b02242f2 100644 --- a/packages/CredentialManager/res/values-mr/strings.xml +++ b/packages/CredentialManager/res/values-mr/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"दुसऱ्या डिव्हाइस वरून"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"वेगळे डिव्हाइस वापरा"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ने विनंती रद्द केली आहे"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml index b933f3e31bdb..f759eedc941d 100644 --- a/packages/CredentialManager/res/values-ms/strings.xml +++ b/packages/CredentialManager/res/values-ms/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Daripada peranti lain"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gunakan peranti yang lain"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Permintaan dibatalkan oleh <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml index c359ce19504f..9c152260fe15 100644 --- a/packages/CredentialManager/res/values-my/strings.xml +++ b/packages/CredentialManager/res/values-my/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"စက်နောက်တစ်ခုမှ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"အခြားစက်သုံးရန်"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"တောင်းဆိုချက်ကို <xliff:g id="APP_NAME">%1$s</xliff:g> က ပယ်ဖျက်လိုက်သည်"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml index 7f63f10494ba..327bc7fdf3b8 100644 --- a/packages/CredentialManager/res/values-nb/strings.xml +++ b/packages/CredentialManager/res/values-nb/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en annen enhet"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Bruk en annen enhet"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Forespørselen er kansellert av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml index 042ed62be9e0..044853a2d880 100644 --- a/packages/CredentialManager/res/values-ne/strings.xml +++ b/packages/CredentialManager/res/values-ne/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"अर्को डिभाइसका लागि"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"अर्कै डिभाइस प्रयोग गरी हेर्नुहोस्"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले अनुरोध रद्द गरेको छ"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml index 68f95a7552e7..83869954ad59 100644 --- a/packages/CredentialManager/res/values-nl/strings.xml +++ b/packages/CredentialManager/res/values-nl/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via een ander apparaat"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Een ander apparaat gebruiken"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Verzoek geannuleerd door <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml index 150ef0bc77ed..9f305ab8ff94 100644 --- a/packages/CredentialManager/res/values-or/strings.xml +++ b/packages/CredentialManager/res/values-or/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ଅନ୍ୟ ଏକ ଡିଭାଇସରୁ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ଏକ ଭିନ୍ନ ଡିଭାଇସ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ଅନୁରୋଧ ବାତିଲ ହୋଇଛି"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml index 10ff1ad3dabc..b90ae5e7bbb7 100644 --- a/packages/CredentialManager/res/values-pa/strings.xml +++ b/packages/CredentialManager/res/values-pa/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ਹੋਰ ਡੀਵਾਈਸ ਤੋਂ"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ਵੱਖਰੇ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਬੇਨਤੀ ਰੱਦ ਕੀਤੀ ਗਈ"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml index be60af589d3e..6966d21ed8e6 100644 --- a/packages/CredentialManager/res/values-pl/strings.xml +++ b/packages/CredentialManager/res/values-pl/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na innym urządzeniu"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Użyj innego urządzenia"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Żądanie anulowane przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml index 93459e64779a..79da371e270b 100644 --- a/packages/CredentialManager/res/values-pt-rBR/strings.xml +++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="4539824758261855508">"Gerenciador de credenciais"</string> + <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string> <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string> <string name="string_continue" msgid="1346732695941131882">"Continuar"</string> <string name="string_more_options" msgid="2763852250269945472">"Salvar de outra forma"</string> @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar um dispositivo diferente"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Solicitação cancelada por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml index 27b84aa96d3c..208b475a073c 100644 --- a/packages/CredentialManager/res/values-pt-rPT/strings.xml +++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use um dispositivo diferente"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Pedido cancelado pela app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml index 93459e64779a..79da371e270b 100644 --- a/packages/CredentialManager/res/values-pt/strings.xml +++ b/packages/CredentialManager/res/values-pt/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="4539824758261855508">"Gerenciador de credenciais"</string> + <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string> <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string> <string name="string_continue" msgid="1346732695941131882">"Continuar"</string> <string name="string_more_options" msgid="2763852250269945472">"Salvar de outra forma"</string> @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar um dispositivo diferente"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Solicitação cancelada por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml index 5292ecaa754d..8984cf2bc2c9 100644 --- a/packages/CredentialManager/res/values-ro/strings.xml +++ b/packages/CredentialManager/res/values-ro/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De pe alt dispozitiv"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Folosește alt dispozitiv"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Solicitare anulată de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml index 99d2d7cc39f8..0f998319de75 100644 --- a/packages/CredentialManager/res/values-ru/strings.xml +++ b/packages/CredentialManager/res/values-ru/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"С другого устройства"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Использовать другое устройство"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" отменило запрос."</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml index 79eaa1390c91..9969a0cceffb 100644 --- a/packages/CredentialManager/res/values-si/strings.xml +++ b/packages/CredentialManager/res/values-si/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"වෙනත් උපාංගයකින්"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"වෙනස් උපාංගයක් භාවිතා කරන්න"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් ඉල්ලීම අවලංගු කරන ලදී"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-sk/strings.xml b/packages/CredentialManager/res/values-sk/strings.xml index 90805a40e002..f91f5466cb90 100644 --- a/packages/CredentialManager/res/values-sk/strings.xml +++ b/packages/CredentialManager/res/values-sk/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z iného zariadenia"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použiť iné zariadenie"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Požiadavku zrušila aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-sl/strings.xml b/packages/CredentialManager/res/values-sl/strings.xml index 16ba2223cd4c..196b0aafc1c3 100644 --- a/packages/CredentialManager/res/values-sl/strings.xml +++ b/packages/CredentialManager/res/values-sl/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Iz druge naprave"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Uporaba druge naprave"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Zahtevo je preklicala aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml index 391c51191daa..5fceff5f783e 100644 --- a/packages/CredentialManager/res/values-sq/strings.xml +++ b/packages/CredentialManager/res/values-sq/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Nga një pajisje tjetër"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Përdor një pajisje tjetër"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Kërkesa u anulua nga <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-sr/strings.xml b/packages/CredentialManager/res/values-sr/strings.xml index b83c69816341..d721a4ba605a 100644 --- a/packages/CredentialManager/res/values-sr/strings.xml +++ b/packages/CredentialManager/res/values-sr/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Са другог уређаја"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Користи други уређај"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Захтве је отказала апликација <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml index 65319b089825..d3099cbc49dc 100644 --- a/packages/CredentialManager/res/values-sv/strings.xml +++ b/packages/CredentialManager/res/values-sv/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via en annan enhet"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Använd en annan enhet"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Begäran avbruten av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-sw/strings.xml b/packages/CredentialManager/res/values-sw/strings.xml index ffb4fa0d307e..1e31128e527a 100644 --- a/packages/CredentialManager/res/values-sw/strings.xml +++ b/packages/CredentialManager/res/values-sw/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Kutoka kwenye kifaa kingine"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Tumia kifaa tofauti"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Ombi lilighairiwa na <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml index 750b67db69b5..1d4e55a0837e 100644 --- a/packages/CredentialManager/res/values-ta/strings.xml +++ b/packages/CredentialManager/res/values-ta/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"மற்றொரு சாதனத்திலிருந்து பயன்படுத்து"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"வேறு சாதனத்தைப் பயன்படுத்து"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸால் கோரிக்கை ரத்துசெய்யப்பட்டது"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml index 064ee960836e..d546b66c92b4 100644 --- a/packages/CredentialManager/res/values-te/strings.xml +++ b/packages/CredentialManager/res/values-te/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"మరొక పరికరం నుండి"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"వేరే పరికరాన్ని ఉపయోగించండి"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g>, రిక్వెస్ట్ను రద్దు చేసింది"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml index 249bd882b694..7ebe82f1c68a 100644 --- a/packages/CredentialManager/res/values-th/strings.xml +++ b/packages/CredentialManager/res/values-th/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"จากอุปกรณ์อื่น"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ใช้อุปกรณ์อื่น"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"ยกเลิกคำขอแล้วโดย <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml index e33f1bf3e283..d069ffe56c6a 100644 --- a/packages/CredentialManager/res/values-tl/strings.xml +++ b/packages/CredentialManager/res/values-tl/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Mula sa ibang device"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gumamit ng ibang device"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Kinansela ng <xliff:g id="APP_NAME">%1$s</xliff:g> ang kahilingan"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml index 4e4894c8e35d..718d7bd3f6cf 100644 --- a/packages/CredentialManager/res/values-tr/strings.xml +++ b/packages/CredentialManager/res/values-tr/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Başka bir cihazdan"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Farklı bir cihaz kullan"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"İstek, <xliff:g id="APP_NAME">%1$s</xliff:g> tarafından iptal edildi"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml index 78a5a5b22bf6..c23933a1ab85 100644 --- a/packages/CredentialManager/res/values-uk/strings.xml +++ b/packages/CredentialManager/res/values-uk/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншого пристрою"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Використовувати інший пристрій"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> скасував запит"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml index d3d5d85d8279..9bb16628053b 100644 --- a/packages/CredentialManager/res/values-ur/strings.xml +++ b/packages/CredentialManager/res/values-ur/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"دوسرے آلے سے"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ایک مختلف آلہ استعمال کریں"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> نے درخواست منسوخ کر دی"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml index a0785b615556..90264e9ebdac 100644 --- a/packages/CredentialManager/res/values-uz/strings.xml +++ b/packages/CredentialManager/res/values-uz/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Boshqa qurilmada"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Boshqa qurilmadan foydalanish"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Soʻrovni <xliff:g id="APP_NAME">%1$s</xliff:g> bekor qilgan"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml index 0e170254ab4b..c6f897e71ab6 100644 --- a/packages/CredentialManager/res/values-vi/strings.xml +++ b/packages/CredentialManager/res/values-vi/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Từ một thiết bị khác"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Dùng thiết bị khác"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> đã huỷ yêu cầu"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml index 495abe660f4d..f5bb10fdf3c1 100644 --- a/packages/CredentialManager/res/values-zh-rCN/strings.xml +++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"通过另一台设备"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他设备"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g>已取消请求"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml index f786254b00d1..ab547937ffaf 100644 --- a/packages/CredentialManager/res/values-zh-rHK/strings.xml +++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已取消要求"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml index c09bf86d3516..f8f8eec59a87 100644 --- a/packages/CredentialManager/res/values-zh-rTW/strings.xml +++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"要求已由 <xliff:g id="APP_NAME">%1$s</xliff:g> 取消"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml index 91f93e26d811..85fe60ab0b3f 100644 --- a/packages/CredentialManager/res/values-zu/strings.xml +++ b/packages/CredentialManager/res/values-zu/strings.xml @@ -92,4 +92,8 @@ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Kusukela kwenye idivayisi"</string> <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Sebenzisa idivayisi ehlukile"</string> <string name="request_cancelled_by" msgid="3735222326886267820">"Isicelo sikhanselwe yi-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) --> + <skip /> + <!-- no translation found for provider_icon_content_description (4023359912607637248) --> + <skip /> </resources> diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt index 325d3f819f14..0fa248de4465 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt @@ -19,7 +19,7 @@ package com.android.credentialmanager import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.credentials.ui.RequestInfo +import android.credentials.selection.RequestInfo import android.util.Log import com.android.credentialmanager.ktx.appLabel import com.android.credentialmanager.ktx.cancelUiRequest diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt index 49387cf410ac..3fbff37e0416 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt @@ -17,8 +17,8 @@ package com.android.credentialmanager.client import android.content.Intent -import android.credentials.ui.BaseDialogResult -import android.credentials.ui.UserSelectionDialogResult +import android.credentials.selection.BaseDialogResult +import android.credentials.selection.UserSelectionDialogResult import com.android.credentialmanager.model.Request import kotlinx.coroutines.flow.StateFlow diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt index 3ef65b052560..ec1f052839e4 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt @@ -18,8 +18,8 @@ package com.android.credentialmanager.client.impl import android.content.Context import android.content.Intent -import android.credentials.ui.BaseDialogResult -import android.credentials.ui.UserSelectionDialogResult +import android.credentials.selection.BaseDialogResult +import android.credentials.selection.UserSelectionDialogResult import android.os.Bundle import android.util.Log import com.android.credentialmanager.TAG diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt index f063074b39b4..a5f227a3adc9 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt @@ -23,9 +23,9 @@ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.credentials.Credential import android.credentials.flags.Flags -import android.credentials.ui.AuthenticationEntry -import android.credentials.ui.Entry -import android.credentials.ui.GetCredentialProviderData +import android.credentials.selection.AuthenticationEntry +import android.credentials.selection.Entry +import android.credentials.selection.GetCredentialProviderData import android.graphics.drawable.Drawable import android.text.TextUtils import android.util.Log diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt index 3abdb6f9c9f2..4155b0398ce0 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt @@ -17,12 +17,12 @@ package com.android.credentialmanager.ktx import android.content.Intent -import android.credentials.ui.CancelUiRequest -import android.credentials.ui.Constants -import android.credentials.ui.CreateCredentialProviderData -import android.credentials.ui.GetCredentialProviderData -import android.credentials.ui.ProviderData -import android.credentials.ui.RequestInfo +import android.credentials.selection.CancelUiRequest +import android.credentials.selection.Constants +import android.credentials.selection.CreateCredentialProviderData +import android.credentials.selection.GetCredentialProviderData +import android.credentials.selection.ProviderData +import android.credentials.selection.RequestInfo import android.os.ResultReceiver val Intent.cancelUiRequest: CancelUiRequest? diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index c0d71494e020..6cafcf7cd976 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -18,16 +18,16 @@ package com.android.credentialmanager import android.content.Context import android.content.Intent -import android.credentials.ui.CancelUiRequest -import android.credentials.ui.Constants -import android.credentials.ui.CreateCredentialProviderData -import android.credentials.ui.GetCredentialProviderData -import android.credentials.ui.DisabledProviderData -import android.credentials.ui.ProviderData -import android.credentials.ui.RequestInfo -import android.credentials.ui.BaseDialogResult -import android.credentials.ui.ProviderPendingIntentResponse -import android.credentials.ui.UserSelectionDialogResult +import android.credentials.selection.CancelUiRequest +import android.credentials.selection.Constants +import android.credentials.selection.CreateCredentialProviderData +import android.credentials.selection.GetCredentialProviderData +import android.credentials.selection.DisabledProviderData +import android.credentials.selection.ProviderData +import android.credentials.selection.RequestInfo +import android.credentials.selection.BaseDialogResult +import android.credentials.selection.ProviderPendingIntentResponse +import android.credentials.selection.UserSelectionDialogResult import android.os.IBinder import android.os.Bundle import android.os.ResultReceiver diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index f8ffc9e26799..fa975aabc867 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -18,8 +18,8 @@ package com.android.credentialmanager import android.app.Activity import android.content.Intent -import android.credentials.ui.BaseDialogResult -import android.credentials.ui.RequestInfo +import android.credentials.selection.BaseDialogResult +import android.credentials.selection.RequestInfo import android.net.Uri import android.os.Bundle import android.os.ResultReceiver @@ -213,7 +213,7 @@ class CredentialSelectorActivity : ComponentActivity() { private fun onInitializationError(e: Exception, intent: Intent) { Log.e(Constants.LOG_TAG, "Failed to show the credential selector; closing the activity", e) val resultReceiver = intent.getParcelableExtra( - android.credentials.ui.Constants.EXTRA_RESULT_RECEIVER, + android.credentials.selection.Constants.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java ) val requestInfo = intent.extras?.getParcelable( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index fc3970de2cee..64595e2642f5 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -20,11 +20,11 @@ import android.content.ComponentName import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager -import android.credentials.ui.CreateCredentialProviderData -import android.credentials.ui.DisabledProviderData -import android.credentials.ui.Entry -import android.credentials.ui.GetCredentialProviderData -import android.credentials.ui.RequestInfo +import android.credentials.selection.CreateCredentialProviderData +import android.credentials.selection.DisabledProviderData +import android.credentials.selection.Entry +import android.credentials.selection.GetCredentialProviderData +import android.credentials.selection.RequestInfo import android.graphics.drawable.Drawable import android.text.TextUtils import android.util.Log diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 985f3228f402..1f1d236f2108 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -25,8 +25,8 @@ import android.credentials.GetCredentialRequest import android.credentials.GetCandidateCredentialsResponse import android.credentials.GetCandidateCredentialsException import android.credentials.CredentialOption +import android.credentials.selection.GetCredentialProviderData import android.graphics.drawable.Icon -import android.credentials.ui.GetCredentialProviderData import android.os.Bundle import android.os.CancellationSignal import android.os.OutcomeReceiver @@ -51,7 +51,6 @@ import android.widget.inline.InlinePresentationSpec import android.credentials.CredentialManager import android.widget.RemoteViews import androidx.autofill.inline.v1.InlineSuggestionUi -import androidx.core.content.ContextCompat import androidx.credentials.provider.CustomCredentialEntry import androidx.credentials.provider.PasswordCredentialEntry import androidx.credentials.provider.PublicKeyCredentialEntry diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt index f40dc7e54870..22a5ec17867a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt @@ -16,7 +16,7 @@ package com.android.credentialmanager.common -import android.credentials.ui.RequestInfo +import android.credentials.selection.RequestInfo enum class DialogType { CREATE_CREDENTIAL, diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt index 4f9fc46e0fce..c9c66b4a7271 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt @@ -17,8 +17,8 @@ package com.android.credentialmanager.ui.screens.single.password import android.content.Intent -import android.credentials.ui.ProviderPendingIntentResponse -import android.credentials.ui.UserSelectionDialogResult +import android.credentials.selection.ProviderPendingIntentResponse +import android.credentials.selection.UserSelectionDialogResult import androidx.activity.result.IntentSenderRequest import androidx.annotation.MainThread import androidx.lifecycle.ViewModel diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt index 3a0e51b24c2c..4f6196648634 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt @@ -64,6 +64,8 @@ import androidx.compose.ui.layout.layoutId import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Constraints @@ -129,7 +131,8 @@ private fun Title(title: String, maxLines: Int = Int.MAX_VALUE) { modifier = Modifier.padding( start = SettingsDimension.itemPaddingAround, end = SettingsDimension.itemPaddingEnd, - ), + ) + .semantics { heading() }, overflow = TextOverflow.Ellipsis, maxLines = maxLines, ) diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index e3a313cad0b8..fd2c076136c0 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Prenttoestel"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Oorfoon"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Randinvoertoestel"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Gehoortoestelle"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi af."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi is ontkoppel."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Kleurregstelling kan nuttig wees wanneer jy die volgende wil doen:<br/> <ol> <li>&nbsp;Om kleure meer akkuraat te sien</li> <li>&nbsp;Om kleure te verwyder om jou te help fokus</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses is onderbreek om battery te beskerm"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Kontroleer tans laaibykomstigheid"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ongeveer <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor gegrond op jou gebruik"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laai tans draadloos"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Laai tans"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Laai nie"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Gekoppel, maar laai nie"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Gelaai"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Volgelaai"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Laai wag tans"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Beheer deur administrateur"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheer deur Beperkte Instellings"</string> <string name="disabled" msgid="8017887509554714950">"Gedeaktiveer"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index ff220fad5950..5afd9dce0079 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"تصوير"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"السمّاعة"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"جهاز إدخال ملحق"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"سماعات أذن طبية"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"بلوتوث"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"تم إيقاف Wi-Fi."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"تم قطع اتصال Wi-Fi."</string> @@ -220,8 +219,8 @@ <item msgid="6946761421234586000">"400%"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"اختيار ملف شخصي"</string> - <string name="category_personal" msgid="6236798763159385225">"التطبيقات الشخصية"</string> - <string name="category_work" msgid="4014193632325996115">"تطبيقات العمل"</string> + <string name="category_personal" msgid="6236798763159385225">"الحسابات الشخصية"</string> + <string name="category_work" msgid="4014193632325996115">"حسابات العمل"</string> <string name="category_private" msgid="4244892185452788977">"ملف شخصي"</string> <string name="category_clone" msgid="1554511758987195974">"استنساخ"</string> <string name="development_settings_title" msgid="140296922921597393">"خيارات المطورين"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"يمكنك الاستفادة من ميزة \"تصحيح الألوان\" من أجل:<br/> <ol> <li>&nbsp;رؤية الألوان بدقة أكبر</li> <li>&nbsp;إزالة الألوان لمساعدتك على التركيز</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - الشحن معلَّق لحماية البطارية"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - يجب فحص ملحق الشحن"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا."</string> <string name="power_discharging_duration" msgid="1076561255466053220">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا، بناءً على استخدامك"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"جارٍ الشحن لاسلكيًا"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"جارٍ الشحن"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"لا يتم الشحن"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"الجهاز متصل ولكن لا يتم شحنه"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"مشحونة"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"البطارية مشحونة بالكامل."</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"الشحن معلَّق"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"إعدادات يتحكم فيها المشرف"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"يتحكّم فيه إعداد محظور"</string> <string name="disabled" msgid="8017887509554714950">"غير مفعّل"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 63154fce12dc..0ee8b897d28e 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Şəkilləndirmə"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Qulaqlıq"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Daxiletmə periferiki"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Eşitmə aparatları"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi sönülüdür."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi bağlantı kəsildi."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Rəng korreksiyası bunları etmək istədikdə faydalı ola bilər:<br/> <ol> <li>&nbsp;Rəngləri daha dəqiq görmək</li> <li>&nbsp;Fokuslanmaq üçün rəngləri ləğv etmək</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyanı qorumaq üçün şarj gözlədilir"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarı yoxlanır"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"İstifadəyə əsasən təxminən <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz şarj edilir"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Şarj edilir"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Doldurulmur"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Qoşulub, amma şarj edilmir"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Şarj edilib"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Tam şarj edilib"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Şarj gözlədilir"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Admin tərəfindən nəzarət olunur"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Məhdudlaşdırılmış Ayar ilə nəzarət edilir"</string> <string name="disabled" msgid="8017887509554714950">"Deaktiv"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 14f3883ef5b5..0cf688703376 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Obrada slika"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Slušalice"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Periferni uređaj za unos"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Slušni aparati"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"WiFi je isključen."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"WiFi veza je prekinuta."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korekcija boja može da bude korisna kada želite:<br/> <ol> <li>&nbsp;Preciznije da vidite boje</li> <li>&nbsp;Da uklonite boje kako biste se fokusirali</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju da bi se zaštitila baterija"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – provera dodatne opreme za punjenje"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Preostalo je oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na osnovu korišćenja"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Uređaj je povezan, ali se ne puni"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Napunjeno do kraja"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Punjenje je na čekanju"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontroliše administrator"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolišu ograničena podešavanja"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 53ffe114f142..b7e90bccf3fb 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Изображения"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Слушалки"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Периферен вход"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Слухови апарати"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi е изключен."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Няма връзка с Wi-Fi."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Функцията за корекция на цветовете може да бъде полезна, когато искате:<br/> <ol> <li>&nbsp;да виждате по-точни цветове;</li> <li>&nbsp;да премахнете цветовете, за да се съсредоточите.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е поставено на пауза с цел запазване на батерията"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Аксесоарът за зареждане се проверява"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Още около <xliff:g id="TIME_REMAINING">%1$s</xliff:g> въз основа на използването"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зарежда се безжично"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Зареждане"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се зарежда"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Свързано, но не се зарежда"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Заредена"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Напълно заредено"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Зареждането е поставено на пауза"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролира се от администратор"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Управлява се чрез ограничена настройка"</string> <string name="disabled" msgid="8017887509554714950">"Деактивирано"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 006849e0fd0d..d2bcfb6d9ed9 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -154,7 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Snimanje"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Slušalice"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Ulazni periferni uređaj"</string> - <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Slušna pomagala"</string> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Slušni aparati"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"WiFi je isključen."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"WiFi nije povezan."</string> @@ -456,8 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Ispravka boja može biti korisna kada želite:<br/> <ol> <li>&nbsp;jasnije vidjeti boje</li> <li>&nbsp;ukloniti boje radi lakšeg fokusiranja</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je pauzirano radi zaštite baterije"</string> - <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjera dodatka za punjenje"</string> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju radi zaštite baterije"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – provjera opreme za punjenje"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Preostalo je još oko <xliff:g id="TIME_REMAINING">%1$s</xliff:g> na osnovu vaše potrošnje"</string> @@ -486,10 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> - <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Povezano, ali se ne puni"</string> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Uređaj je povezan, ali se ne puni"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Potpuno napunjeno"</string> - <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Punjenje na čekanju"</string> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Punjenje je na čekanju"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pod kontrolom administratora"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 6e95b3b6ccaa..a556cf155dce 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Imatges"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Auricular"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Perifèric d\'entrada"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Audiòfons"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi desactivada."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi desconnectada."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"La correcció de color pot ser útil si vols:<br/> <ol> <li>&nbsp;Veure els colors amb més precisió.</li> <li>&nbsp;Suprimir els colors per concentrar-te millor.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: la càrrega s\'ha posat en espera per protegir la bateria"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està comprovant l\'accessori de càrrega"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Temps restant aproximat: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregant sense fil"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"S\'està carregant"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No s\'està carregant"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connectat, però sense carregar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Totalment carregada"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Càrrega en espera"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlat per l\'administrador"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlat per la configuració restringida"</string> <string name="disabled" msgid="8017887509554714950">"Desactivat"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 96ee652f8737..45e089ee0f54 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Zobrazovací zařízení"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Sluchátka"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Periferní vstupní zařízení"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Naslouchátka"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Síť Wi-Fi je vypnuta."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Síť Wi-Fi je odpojena."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korekce barev se může hodit, když chcete:<br/> <ol> <li>&nbsp;Vidět barvy přesněji.</li> <li>&nbsp;Odstranit barvy kvůli zlepšení soustředění.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení je pozastaveno za účelem ochrany baterie"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Kontrola nabíjecího příslušenství"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Při vašem obvyklém využití zbývá asi <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezdrátové nabíjení"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Nabíjení"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíjí se"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Připojeno, ale nenabíjí se"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Nabito"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Plně nabito"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Nabíjení pozastaveno"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Spravováno administrátorem"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Spravováno omezeným nastavením"</string> <string name="disabled" msgid="8017887509554714950">"Deaktivováno"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 61d0315e6d95..89d34a6cf201 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Billede"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Hovedtelefoner"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Eksterne inputenheder"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Høreapparater"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi er slået fra."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi er afbrudt."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Farvekorrigering kan være en nyttig funktion, når du vil:<br/> <ol> <li>&nbsp;Se farver mere nøjagtigt</li> <li>&nbsp;Fjerne farver, så du nemmere kan fokusere</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Opladningen er sat på pause for at beskytte batteriet"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tjekker opladningstilbehøret"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ca. <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage, alt efter hvordan du bruger enheden"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Trådløs opladning"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Oplader"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Oplader ikke"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Forbundet, men oplader ikke"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Opladet"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fuldt opladet"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Opladningen er blevet sat på pause"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolleret af administratoren"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styres af en begrænset indstilling"</string> <string name="disabled" msgid="8017887509554714950">"Deaktiveret"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 2e4e507ed903..b7df3688e24f 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Απεικόνιση"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Ακουστικά"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Περιφερειακό εισόδου"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Βοηθήματα ακοής"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi ανενεργό."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Το Wi-Fi έχει αποσυνδεθεί."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Η διόρθωση χρωμάτων μπορεί να σας φανεί χρήσιμη όταν θέλετε:<br/> <ol> <li>&nbsp;Μεγαλύτερη ακρίβεια στην απεικόνιση χρωμάτων</li> <li>&nbsp;Να καταργήσετε χρώματα για να συγκεντρωθείτε</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Η φόρτιση τέθηκε σε αναμονή για προστασία της μπαταρίας"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Έλεγχος αξεσουάρ φόρτισης"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Απομένει/ουν περίπου <xliff:g id="TIME_REMAINING">%1$s</xliff:g>, βάσει της χρήσης σας"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ασύρματη φόρτιση"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Φόρτιση"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Δεν φορτίζει"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Συνδέθηκε, αλλά δεν φορτίζει"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Φορτισμένη"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Πλήρως φορτισμένο"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Η φόρτιση τέθηκε σε αναμονή"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ελέγχονται από το διαχειριστή"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ελέγχεται από τη Ρύθμιση με περιορισμό"</string> <string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένο"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 3c6e70e4d360..66aaaf5ab9bc 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Imaging"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Headphone"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Input Peripheral"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Hearing aids"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi off."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi disconnected."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Colour correction can be helpful when you want to:<br/> <ol> <li>&nbsp;See colours more accurately</li> <li>&nbsp;Remove colours to help you focus</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Checking charging accessory"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Charging"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connected, but not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully charged"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 3c6e70e4d360..66aaaf5ab9bc 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Imaging"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Headphone"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Input Peripheral"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Hearing aids"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi off."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi disconnected."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Colour correction can be helpful when you want to:<br/> <ol> <li>&nbsp;See colours more accurately</li> <li>&nbsp;Remove colours to help you focus</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Checking charging accessory"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Charging"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connected, but not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully charged"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 3c6e70e4d360..66aaaf5ab9bc 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Imaging"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Headphone"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Input Peripheral"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Hearing aids"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi off."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi disconnected."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Colour correction can be helpful when you want to:<br/> <ol> <li>&nbsp;See colours more accurately</li> <li>&nbsp;Remove colours to help you focus</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Charging on hold to protect battery"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Checking charging accessory"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"About <xliff:g id="TIME_REMAINING">%1$s</xliff:g> left based on your usage"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Charging"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connected, but not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fully charged"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Charging on hold"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 19f8b215c841..5e496cd75c3d 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -181,7 +181,7 @@ <string name="launch_defaults_none" msgid="8049374306261262709">"Sin configuraciones predeterminadas"</string> <string name="tts_settings" msgid="8130616705989351312">"Configuración de texto a voz"</string> <string name="tts_settings_title" msgid="7602210956640483039">"Salida de texto a voz"</string> - <string name="tts_default_rate_title" msgid="3964187817364304022">"Velocidad de voz"</string> + <string name="tts_default_rate_title" msgid="3964187817364304022">"Velocidad de habla"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"Velocidad en la que se habla el texto"</string> <string name="tts_default_pitch_title" msgid="6988592215554485479">"Tono"</string> <string name="tts_default_pitch_summary" msgid="9132719475281551884">"Afecta el tono de la voz sintetizada"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 8334379a9fb2..3ce371f9056c 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Escáner"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Auriculares"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Periférico de entrada"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Audífonos"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi desactivado."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi desconectado."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corrección de color puede ser útil si quieres:<br/> <ol> <li>&nbsp;Ver los colores mejor</li> <li>&nbsp;Quitar los colores para concentrarte mejor</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>: <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga pausada para proteger la batería"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Comprobando accesorio de carga"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tiempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tiempo restante aproximado según tu uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Cargando"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Conectado, pero sin cargar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carga pausada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlado por ajustes restringidos"</string> <string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 35fd2dae93d3..115a64a05749 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Pildindus"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Kõrvaklapid"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Sisestatud välisseade"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Kuuldeaparaadid"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"WiFi on välja lülitatud."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"WiFi-ühendus on katkestatud."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Värvide korrigeerimisest võib abi olla, kui soovite:<br/> <ol> <li>&nbsp;värve täpsemalt näha;</li> <li>&nbsp;värve eemaldada, et paremini keskenduda.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on aku kaitsmiseks ootele pandud"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimistarviku kontrollimine"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Teie kasutuse põhjal on jäänud ligikaudu <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Juhtmevaba laadimine"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Laadimine"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Ühendatud, kuid ei laadita"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Laetud"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Täielikult laetud"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Laadimine on ootele pandud"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Juhib administraator"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Haldavad piiranguga seaded"</string> <string name="disabled" msgid="8017887509554714950">"Keelatud"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 89ab3a48454d..f1e507e5c2f8 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Irudietarako gailua"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Entzungailua"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Idazteko gailua"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Audifonoak"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth bidezko gailua"</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> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Baliteke koloreen zuzenketa lagungarria izatea hauek egin nahi dituzunean:<br/> <ol> <li>&nbsp;Koloreak zehaztasun handiagoz ikusi.</li> <li>&nbsp;Koloreak kendu, arreta gal ez dezazun.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: kargatze-prozesua zain dago bateria babesteko"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>: kargatzeko osagarria egiaztatzen"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Erabilera kontuan izanda, <xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hari gabe kargatzen"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Kargatzen"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ez da kargatzen ari"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Konektatuta dago, baina ez da kargatzen ari"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Kargatuta"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Erabat kargatuta"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Kargatze-prozesua zain dago"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administratzaileak kontrolatzen du"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ezarpen mugatuak kontrolatzen du"</string> <string name="disabled" msgid="8017887509554714950">"Desgaituta"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index cb6d27be8703..5d23510c7635 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Imagerie"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Écouteurs"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Périphérique d\'entrée"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Prothèses auditives"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi désactivé."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi déconnecté."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"La correction des couleurs peut être utile lorsque vous souhaitez :<br/> <ol> <li> voir les couleurs avec plus de précision;</li> <li> retirer les couleurs pour vous aider à vous concentrer.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> : <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – La recharge a été mise en pause pour protéger la pile"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Vérification de l\'accessoire de recharge en cours…"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Il reste environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> en fonction de votre usage"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En recharge sans fil"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Recharge en cours…"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"N\'est pas en charge"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connecté, mais ne se recharge pas"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Complètement rechargée"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Recharge en pause"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Contrôlé par l\'administrateur"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Contrôlé par les paramètres restreints"</string> <string name="disabled" msgid="8017887509554714950">"Désactivée"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 11d083884ba9..75eb21fbc86d 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Dispositivo de imaxe"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Auriculares"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Periférico de entrada"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Audiófonos"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi desactivada."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi desconectada."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"A corrección da cor pode serche útil se queres:<br/> <ol> <li>&nbsp;Ver mellor as cores</li> <li>&nbsp;Quitar as cores para concentrarte mellor</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>. A carga púxose en pausa para protexer a batería"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>. Comprobando accesorio de carga"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo restante aproximado: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tempo restante aproximado (<xliff:g id="LEVEL">%2$s</xliff:g>): <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo restante aproximado en función do uso: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sen fíos"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Cargando"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Non se está cargando"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Conectado, pero non cargando"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carga en pausa"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Opción controlada polo administrador"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Baixo o control de opcións restrinxidas"</string> <string name="disabled" msgid="8017887509554714950">"Desactivada"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index ebd02c3b510c..e4770634aa6e 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"ઇમેજિંગ"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"હેડફોન"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ઇનપુટ પેરિફેરલ"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"સાંભળવામાં મદદ આપતા યંત્રો"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"બ્લૂટૂથ"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi બંધ."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi ડિસ્કનેક્ટ થયું."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"રંગમાં સુધારણા કરવાની સુવિધાનો ઉપયોગ ત્યારે સહાયરૂપ બની શકે છે કે જ્યારે તમે આ કરવા માગતા હો:<br/> <ol> <li>&nbsp;વધુ સચોટપણે રંગ જોવા</li> <li>&nbsp;ફોકસ કરવામાં સહાય માટે અમુક રંગ કાઢી નાખવા</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીને સુરક્ષિત રાખવા માટે, ચાર્જિંગ હોલ્ડ પર રાખવામાં આવ્યું છે"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઍક્સેસરી ચેક કરી રહ્યાં છીએ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"તમારા વપરાશના આધારે લગભગ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> બાકી છે"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"વાયરલેસથી ચાર્જિંગ"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ચાર્જ થઈ રહ્યું છે"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ચાર્જ થઈ રહ્યું નથી"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"કનેક્ટેડ છે, પરંતુ ચાર્જ થઈ રહ્યું નથી"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ચાર્જ થયું"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"સંપૂર્ણપણે ચાર્જ છે"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ચાર્જિંગ હોલ્ડ પર રાખવામાં આવ્યું"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"પ્રતિબંધિત સેટિંગ દ્વારા નિયંત્રિત"</string> <string name="disabled" msgid="8017887509554714950">"અક્ષમ કર્યો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index e03de1572c34..0e91a37554bb 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"इमेजिंग"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"हेडफ़ोन"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"इनपुट पेरिफ़ेरल"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"कान की मशीनें"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ब्लूटूथ"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"वाई-फ़ाई बंद है."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"वाई-फ़ाई डिसकनेक्ट है."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"रंग में सुधार करने की सुविधा का इस्तेमाल, इन मामलों में किया जा सकता है:<br/> <ol> <li>&nbsp;आपको ज़्यादा सटीक तरह से रंग देखने हों</li> <li>&nbsp;ज़्यादा फ़ोकस करने के लिए, आपको कुछ खास रंग हटाने हों</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - बैटरी को सुरक्षित रखने के लिए, फ़ोन को चार्ज होने से रोक दिया गया है"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऐक्सेसरी की जांच की जा रही है"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"आपके इस्तेमाल के हिसाब से बैटरी करीब <xliff:g id="TIME_REMAINING">%1$s</xliff:g> में खत्म हो जाएगी"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस चार्जिंग"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"चार्ज हो रहा है"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज नहीं हो रही है"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"फ़ोन कनेक्ट हो गया, लेकिन चार्ज नहीं हो रहा है"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"बैटरी चार्ज हो गई"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"बैटरी पूरी चार्ज है"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"फ़ोन को चार्ज होने से रोक दिया गया है"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"इसका नियंत्रण एडमिन के पास है"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"इसे पाबंदी मोड वाली सेटिंग से कंट्रोल किया जाता है"</string> <string name="disabled" msgid="8017887509554714950">"बंद किया गया"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 8bc6f667f241..1b28a0d36934 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Պատկերներ"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Ականջակալ"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Մուտքի արտաքին սարքեր"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Լսողական սարք"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi-ն անջատված է:"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi-ը կապակցված չէ:"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Գունաշտկումը կարող է օգնել, երբ դուք ուզում եք՝<br/> <ol> <li>&nbsp;Ավելի հստակ տեսնել գույները</li> <li>&nbsp;Հեռացնել գույները, որպեսզի կարողանաք կենտրոնանալ</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու համար"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորման սարքը ստուգվում է"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Լիցքը կբավարարի մոտ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>՝ կախված օգտագործման եղանակից"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Անլար լիցքավորում"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Լիցքավորում"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Չի լիցքավորվում"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Սարքը միացած է, սակայն չի լիցքավորվում"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Լիցքավորված է"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Լրիվ լիցքավորված է"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Լրցքավորումը դադարեցված է"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Վերահսկվում է ադմինիստրատորի կողմից"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Կառավարվում է սահմանափակ ռեժիմի կարգավորումներով"</string> <string name="disabled" msgid="8017887509554714950">"Կասեցված է"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 2f27c62c0e4e..9e9c79cea726 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Pencitraan"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Headphone"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Periferal Masukan"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Alat Bantu Dengar"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi tidak aktif."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi tidak terhubung."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Koreksi warna dapat berguna jika Anda ingin:<br/> <ol> <li> Melihat warna dengan lebih akurat</li> <li> Menghapus warna agar Anda lebih fokus</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dihentikan sementara untuk melindungi baterai"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Memeriksa aksesori pengisi daya"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Sekitar <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi berdasarkan penggunaan Anda"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengisi daya nirkabel"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Pengisian daya"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengisi daya"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Terhubung, tetapi tidak mengisi daya"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Terisi"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Baterai Terisi Penuh"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Pengisian daya dihentikan sementara"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikontrol oleh admin"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikontrol oleh Setelan Terbatas"</string> <string name="disabled" msgid="8017887509554714950">"Dinonaktifkan"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index c2d6de3fae42..071597ac31b8 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Myndherming"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Heyrnartól"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Jaðartæki með inntak"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Heyrnartæki"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Slökkt á Wi-Fi."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi ótengt."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Litaleiðrétting kemur m.a. að gagni þegar þú vilt:<br/> <ol> <li>&nbsp;Sjá liti í aukinni skerpu</li> <li>&nbsp;Fjarlægja liti til að geta einbeitt þér betur</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla í bið til að vernda rafhlöðuna"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Athugar hleðslutæki"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Um það bil <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir miðað við notkun þína"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hleður þráðlaust"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Í hleðslu"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ekki í hleðslu"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Tækið er tengt en hleðst ekki"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Fullhlaðin"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Full hleðsla"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Hleðsla í bið"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Stjórnað af kerfisstjóra"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Stýrt af takmarkaði stillingu"</string> <string name="disabled" msgid="8017887509554714950">"Óvirkt"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 27b23b7d79bf..05404322be12 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Sistema di imaging"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Cuffie"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Periferica di immissione"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Apparecchi acustici"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi non attivo."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Rete Wi-Fi scollegata."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"La correzione del colore può essere utile quando vuoi:<br/> <ol> <li>&nbsp;Vedere i colori con più precisione</li> <li>&nbsp;Rimuovere i colori per mettere meglio a fuoco</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica in sospeso per proteggere la batteria"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Controllo dell\'accessorio di ricarica"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Tempo rimanente: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Tempo rimanente in base al tuo utilizzo: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> circa"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"In carica, wireless"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"In carica"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Non in carica"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connesso, ma non in carica"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carica"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Batteria completamente carica"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ricarica in sospeso"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Gestita dall\'amministratore"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gestita tramite impostazioni con restrizioni"</string> <string name="disabled" msgid="8017887509554714950">"Disattivato"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 0c3c4f72945d..6dbf7abe9ebe 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"הדמיה"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"אוזניות"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ציוד קלט היקפי"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"מכשירי שמיעה"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi כבוי."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi מנותק."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"תיקון הצבע יכול לעזור אם רוצים:<br/> <ol> <li>&nbsp;לראות צבעים מדויקים יותר</li> <li>&nbsp;לראות פחות צבעים כדי לשפר את הריכוז</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה הושהתה כדי להגן על הסוללה"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – מתבצעת בדיקה של אביזר הטעינה"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"הזמן הנותר על סמך השימוש שלך: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"בטעינה אלחוטית"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"טעינה"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"לא בטעינה"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"המכשיר מחובר אבל לא נטען"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"הסוללה טעונה"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"טעונה במלואה"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"הטעינה הושהתה"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"נמצא בשליטת מנהל מערכת"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"בשליטה של הגדרה מוגבלת"</string> <string name="disabled" msgid="8017887509554714950">"מושבת"</string> @@ -527,7 +522,7 @@ <string name="ims_reg_title" msgid="8197592958123671062">"סטטוס הרשמה ל-IMS"</string> <string name="ims_reg_status_registered" msgid="884916398194885457">"רשום"</string> <string name="ims_reg_status_not_registered" msgid="2989287366045704694">"לא רשום"</string> - <string name="status_unavailable" msgid="5279036186589861608">"לא זמין"</string> + <string name="status_unavailable" msgid="5279036186589861608">"לא זמינה"</string> <string name="wifi_status_mac_randomized" msgid="466382542497832189">"כתובת ה-MAC אקראית"</string> <string name="wifi_tether_connected_summary" msgid="5100712926640492336">"{count,plural, =1{מכשיר אחד מחובר}one{# מכשירים מחוברים}two{# מכשירים מחוברים}other{# מכשירים מחוברים}}"</string> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"יותר זמן."</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 3f71b391303b..cc560b896af9 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Бейне құралы"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Құлақаспап"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Кіріс құралы"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Есту аппараттары"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi өшірулі."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi ажыратылған."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Түсті түзету мына жағдайларда пайдалы болуы мүмкін:<br/> <ol> <li>&nbsp;түстерді анығырақ көру;</li> <li>&nbsp;зейін қоюға көмектесу үшін түстерді алып тастау.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>: батареяны қорғау үшін зарядтау кідіртіледі."</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>: зарядтау құрылғысы тексеріледі."</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Пайдалану деректеріңізге сәйкес енді шамамен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Сымсыз зарядталуда"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Зарядталып жатыр."</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Зарядталу орындалып жатқан жоқ"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Құрылғы жалғанған, бірақ зарядталып жатқан жоқ."</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Зарядталды"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Толық зарядталды."</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Зарядтау кідіртілді."</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Әкімші басқарады"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Шектелген параметрлер арқылы басқарылады."</string> <string name="disabled" msgid="8017887509554714950">"Өшірілген"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index e7f0220d3cc6..0288e9207e0d 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"ಇಮೇಜಿಂಗ್"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"ಹೆಡ್ಫೋನ್"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ಪೆರಿಪೆರಲ್ ಇನ್ಪುಟ್"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ಬ್ಲೂಟೂತ್"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"ವೈಫೈ ಆಫ್."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"ವೈಫೈ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ನೀವು ಹೆಚ್ಚು ಸ್ಪಷ್ಟವಾದ ಬಣ್ಣಗಳನ್ನು ನೋಡಲು ಬಯಸಿದರೆ :<br/> <ol> <li>&nbsp; ಬಣ್ಣದ ತಿದ್ದುಪಡಿಯು ಸಹಾಯಕವಾಗಿರುತ್ತದೆ; ಗಮನವನ್ನು ಕೇಂದ್ರೀಕರಿಸಲು ನಿಮಗೆ ಸಹಾಯ ಮಾಡಲು ಬಣ್ಣಗಳನ್ನು ತೆಗೆದುಹಾಕಿ </li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯನ್ನು ರಕ್ಷಿಸಲು ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಹೋಲ್ಡ್ ಮಾಡಲಾಗಿದೆ"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಪರಿಕರವನ್ನು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"(<xliff:g id="LEVEL">%2$s</xliff:g>) ತಲುಪಲು <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ನಿಮ್ಮ ಬಳಕೆಯ ಆಧಾರದ ಮೇಲೆ ಸುಮಾರು <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ವೈರ್ಲೆಸ್ ಚಾರ್ಜಿಂಗ್"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ, ಆದರೆ ಚಾರ್ಜಿಂಗ್ ಆಗುತ್ತಿಲ್ಲ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"ಪೂರ್ಣವಾಗಿ ಚಾರ್ಜ್ ಆಗಿದೆ"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಹೋಲ್ಡ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗುತ್ತದೆ"</string> <string name="disabled" msgid="8017887509554714950">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index df4349417358..432f4ed4d8fe 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"이미징"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"헤드폰"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"입력 주변기기"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"보청기"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"블루투스"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi가 꺼져 있습니다."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi 연결이 끊어졌습니다."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"색상 보정은 다음과 같은 경우에 유용할 수 있습니다.<br/> <ol> <li>&nbsp;색상을 보다 정확하게 확인하려는 경우</li> <li>&nbsp;집중에 도움이 되도록 색상을 제거하려는 경우</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>, <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 배터리 보호를 위해 충전 일시중지"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 액세서리 확인 중"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"남은 시간: 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"남은 시간 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"내 사용량을 기준으로 약 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> 남음"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"무선 충전 중"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"충전"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"충전 안함"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"연결되었으나 충전 중이 아님"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"충전됨"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"완전히 충전됨"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"충전 일시중지"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"관리자가 제어"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"제한된 설정으로 제어됨"</string> <string name="disabled" msgid="8017887509554714950">"사용 안함"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 6e6926edf98a..aa685a20816e 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Сүрөт тартуучу түзмөк"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Кулакчын"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Дайындарды киргизүүчү тышкы түзмөк"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Угуу аппараттары"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi өчүк."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi туташуусу жок."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Түстөрдү тууралоо менен:<br/> <ol> <li>&nbsp;Керектүү түстөрдү аласыз</li> <li>&nbsp;Алагды кылган түстөрдү өчүрүп саласыз</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны коргоо үчүн кубаттоо тындырылды"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубаттоо шайманы текшерилүүдө"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Колдонгонуңузга караганда болжол менен <xliff:g id="TIME_REMAINING">%1$s</xliff:g> калды"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зымсыз кубатталууда"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Кубатталууда"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Кубат алган жок"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Туташкан, бирок кубатталбай жатат"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Кубатталды"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Толук кубатталды"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Кубаттоо күтүү режиминде"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Администратор тарабынан көзөмөлдөнөт"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Чектелген параметр аркылуу көзөмөлдөнөт"</string> <string name="disabled" msgid="8017887509554714950">"Өчүрүлгөн"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 5e2f696ab0cd..d0d95075d5a3 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Vaizdavimo įrenginys"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Ausinės"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Išorinis įvesties įrenginys"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Klausos aparatai"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"„Wi-Fi“ išjungtas."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"„Wi-Fi“ atjungtas."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Spalvų taisymas gali būti naudingas, kai norite:<br/> <ol> <li>&nbsp;aiškiau matyti spalvas;</li> <li>&nbsp;pašalinti spalvas, kad galėtumėte sutelkti dėmesį.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas pristabdytas, siekiant apsaugoti akumuliatorių"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – tikrinamas įkrovimo priedas"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Liko maždaug <xliff:g id="TIME_REMAINING">%1$s</xliff:g>, atsižvelgiant į naudojimą"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kraunama be laidų"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Įkraunama"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nekraunama"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Prijungta, bet nekraunama"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Įkrauta"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Visiškai įkrautas"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Įkrovimas pristabdytas"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Valdo administratorius"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Valdoma pagal apribotą nustatymą"</string> <string name="disabled" msgid="8017887509554714950">"Neleidžiama"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index f9bab6fe0d4a..43a940b0fad9 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Attēlu apstrādes ierīce"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Austiņas"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Ievades ierīce"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Dzirdes aparāti"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi savienojums izslēgts"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi savienojums pārtraukts"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Krāsu korekcija var būt noderīga šādiem mērķiem:<br/> <ol> <li>&nbsp;precīzākai krāsu attēlošanai;</li> <li>&nbsp;krāsu noņemšanai, lai būtu vieglāk koncentrēties.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> — <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde apturēta, lai aizsargātu akumulatoru"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> — notiek uzlādes piederuma pārbaude"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Aptuvenais atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Aptuvenais atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Ņemot vērā lietojumu, atlikušais laiks: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezvadu uzlāde"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Notiek uzlāde"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenotiek uzlāde"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Savienota, taču netiek uzlādēta"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Uzlādēts"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Pilnībā uzlādēts"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Uzlāde apturēta"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolē administrators"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolē ierobežots iestatījums"</string> <string name="disabled" msgid="8017887509554714950">"Atspējots"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index aafa8463871f..db5bdf4bd279 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Слики"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Слушалка"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Периферен влез"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Слушни помагала"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi е исклучено."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi е исклучено."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Корекцијата на боите може да биде корисна кога сакате:<br/> <ol> <li>&nbsp;да ги гледате боите попрецизно</li> <li>&nbsp;да ги отстраните боите за полесно да се концентрирате</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - полнењето е паузирано за да се заштити батеријата"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - се проверува додатокот за полнење"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Уште околу <xliff:g id="TIME_REMAINING">%1$s</xliff:g> според вашето користење"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Се полни безжично"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Се полни"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се полни"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Поврзано, но не се полни"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Полна"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Целосно полна"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Полнењето е паузирано"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролирано од администраторот"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролирано со ограничени поставки"</string> <string name="disabled" msgid="8017887509554714950">"Оневозможено"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index c857a2b3d9e8..787ce976e236 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Зураглал"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Чихэвч"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Нэмэлт оролт"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Сонсголын төхөөрөмжүүд"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi унтраалттай байна."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi холбогдоогүй байна."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгө тохируулга нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:<br/> <ol> <li>&nbsp;Өнгөнүүдийг илүү нарийвчилж харах</li> <li>&nbsp;Төвлөрөхийн тулд өнгөнүүдийг хасах</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг хамгаалахын тулд цэнэглэхийг хүлээлгэсэн"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх нэмэлт хэрэгслийг шалгаж байна"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Таны хэрэглээнд үндэслэн ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Утасгүй цэнэглэж байна"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Цэнэглэж байна"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Цэнэглэхгүй байна"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Холбогдсон ч цэнэглэхгүй байна"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Цэнэглэсэн"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Бүрэн цэнэглэсэн"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Цэнэглэхийг хүлээлгэд оруулсан"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Админ удирдсан"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Хязгаарлагдсан тохиргоогоор хянадаг"</string> <string name="disabled" msgid="8017887509554714950">"Идэвхгүйжүүлсэн"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 26669b9df4b3..00482ea8a15f 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"इमेज"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"हेडफोन"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"इनपुट परिधीय"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"श्रवणयंत्रे"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ब्लूटूथ"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"वाय-फाय बंद."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"वाय-फाय डिस्कनेक्ट झाले."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"तुम्हाला पुढील गोष्टी करायच्या असतील, तेव्हा रंग सुधारणेची मदत होऊ शकते:<br/> <ol> <li>&nbsp;रंग आणखी अचूकपणे पाहण्यासाठी</li> <li>&nbsp;तुम्हाला लक्ष केंद्रित करण्यात मदत करण्याकरिता रंग काढून टाकण्यासाठी</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरीचे संरक्षण करण्यासाठी चार्जिंग थांबवले आहे"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंगसंबंधित ॲक्सेसरी तपासत आहे"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"तुमच्या वापरावर आधारित अंदाजे <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाकी आहे"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेसने चार्ज होत आहे"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"चार्ज होत आहे"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज होत नाही"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"कनेक्ट केलेले आहे, पण चार्ज होत नाही"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज झाली"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"पूर्ण चार्ज झाली"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"चार्जिंग थांबवले आहे"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"प्रशासकाने नियंत्रित केलेले"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबंधित केलेल्या सेटिंग द्वारे नियंत्रित"</string> <string name="disabled" msgid="8017887509554714950">"अक्षम"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index dd31c8554364..bd3eb997183d 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Pengimejan"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Fon kepala"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Persisian Input"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Alat Bantu Pendengaran"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi dimatikan."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi diputuskan sambungannya."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Pembetulan warna dapat membantu apabila anda mahu:<br/> <ol> <li>&nbsp;Melihat warna dengan lebih tepat</li> <li>&nbsp;Mengalih keluar warna agar anda dapat menumpukan perhatian</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan ditunda untuk melindungi bateri"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Memeriksa aksesori pengecasan"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Kira-kira <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi berdasarkan penggunaan anda"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengecas tanpa wayar"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Pengecasan"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengecas"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Disambungkan tetapi tidak dicas"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Sudah dicas"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Dicas Penuh"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Pengecasan ditunda"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikawal oleh pentadbir"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikawal oleh Tetapan Terhad"</string> <string name="disabled" msgid="8017887509554714950">"Dilumpuhkan"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 7c74e1bb1385..c18b273bd9bf 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"ဓာတ်ပုံဆိုင်ရာ"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"နားကြပ်"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ချိတ်ဆက်အသုံးပြုရသည့် စက်ပစ္စည်းများ"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"နားကြားကိရိယာ"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ဘလူးတုသ်"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi ပိတ်ထားသည်"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi ချိတ်ဆက်ထားမှု မရှိပါ"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"အရောင် အမှန်ပြင်ခြင်းသည် အောက်ပါတို့အတွက် အသုံးဝင်နိုင်သည်-<br/> <ol> <li>&nbsp;အရောင်များကို ပိုမိုမှန်ကန်စွာ ကြည့်ရှုခြင်း</li> <li>&nbsp;အာရုံစိုက်နိုင်ရန် အရောင်များ ဖယ်ရှားခြင်း</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီကာကွယ်ရန် အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းပစ္စည်း စစ်ဆေးနေသည်"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"သင်၏ အသုံးပြုမှု အပေါ် မူတည်၍ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ခန့် ကျန်သည်"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ကြိုးမဲ့ အားသွင်းနေသည်"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"အားသွင်းနေသည်"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"အားသွင်းမနေပါ"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"ချိတ်ဆက်ထားသော်လည်း အားသွင်းမနေပါ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"အားသွင်းပြီးပါပြီ"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"အားအပြည့်သွင်းထားသည်"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"စီမံခန့်ခွဲသူမှ ထိန်းချုပ်ပါသည်"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ကန့်သတ်ထားသော ဆက်တင်များဖြင့် ထိန်းချုပ်ထားသည်"</string> <string name="disabled" msgid="8017887509554714950">"ပိတ်ထားပြီး"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 4782f269158a..b3c429502e72 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Bildefremviser"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Øretelefoner"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Inndata fra ytre utstyrsenheter"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Høreapparater"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi er av."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi er frakoblet."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Fargekorrigering kan være nyttig når du vil<br/> <ol> <li>&nbsp;se farger mer nøyaktig</li> <li>&nbsp;fjerne farger for å gjøre det enklere å fokusere</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er satt på vent for å beskytte batteriet"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Sjekker ladetilbehøret"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Omtrent <xliff:g id="TIME_REMAINING">%1$s</xliff:g> igjen basert på bruken din"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Lader trådløst"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Lader"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Lader ikke"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Tilkoblet, men lader ikke"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Ladet"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fulladet"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ladingen er satt på vent"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrollert av administratoren"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollert av en begrenset innstilling"</string> <string name="disabled" msgid="8017887509554714950">"Slått av"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 3a7da748c247..377078deeb76 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"छवि सम्बन्धी"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"हेडफोन"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"इनपुट सम्बन्धी बाह्य यन्त्र"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"श्रवण यन्त्रहरू"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ब्लुटुथ"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi बन्द।"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi जडान विच्छेद भयो।"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"तपाईं रङ सच्याउने सुविधाका सहायताले निम्न कार्य गर्न सक्नुहुन्छ:<br/> <ol> <li>&nbsp;अझ सटीक तरिकाले रङहरू हेर्न</li> <li>&nbsp;फोकस गर्नका लागि रङहरू हटाउन</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ब्याट्री जोगाउन चार्जिङ होल्ड गरिएको छ"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिङ एक्सेसरीको जाँच गरिँदै छ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"तपाईंको प्रयोगको आधारमा लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस तरिकाले चार्ज गरिँदै छ"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"चार्ज हुँदै छ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज भइरहेको छैन"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"जोडिएको छ तर चार्ज गरिराखिएको छैन"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज भयो"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"पूर्ण रूपमा चार्ज भएको छ"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"चार्जिङ होल्ड गरिएको छ"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"प्रशासकद्वारा नियन्त्रित"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबन्धित सेटिङले नियन्त्रण गरेको"</string> <string name="disabled" msgid="8017887509554714950">"असक्षम पारियो"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index ea245c4812b1..30bb05012dd4 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"ଇମେଜିଙ୍ଗ"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"ହେଡ୍ଫୋନ୍"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ଇନ୍ପୁଟ୍ ଉପକରଣ"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ବ୍ଲୁଟୁଥ"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"ୱାଇ-ଫାଇ ବନ୍ଦ।"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"ୱାଇଫାଇ ବିଚ୍ଛିନ୍ନ କରାଗଲା।"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ଆପଣ ଏସବୁ କରିବାକୁ ଚାହିଁଲେ ରଙ୍ଗ ସଂଶୋଧନ ଉପଯୋଗୀ ହୋଇପାରିବ:<br/> <ol> <li>&nbsp;ଆହୁରି ସଠିକ୍ ଭାବେ ରଙ୍ଗଗୁଡ଼ିକ ଦେଖିବା</li> <li>&nbsp;ଆପଣଙ୍କୁ ଫୋକସ କରିବାରେ ସାହାଯ୍ୟ କରିବା ପାଇଁ ରଙ୍ଗଗୁଡ଼ିକୁ କାଢ଼ିବା</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍ରାଇଡ୍ କରାଯାଇଛି"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବେଟେରୀକୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂ ଆକସେସୋରୀକୁ ଯାଞ୍ଚ କରାଯାଉଛି"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ପାଇଁ (<xliff:g id="LEVEL">%2$s</xliff:g>) ବଳକା ଅଛି"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ଆପଣଙ୍କ ବ୍ୟବହାରକୁ ଆଧାର କରି ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ୱେୟରଲେସ ଭାବେ ଚାର୍ଜିଂ"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ଚାର୍ଜ ହେଉଛି"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ଚାର୍ଜ ହେଉନାହିଁ"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"କନେକ୍ଟ ହୋଇଛି, କିନ୍ତୁ ଚାର୍ଜ ହେଉନାହିଁ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ଚାର୍ଜ ହୋଇଯାଇଛି"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ ଚାର୍ଜ ହୋଇଛି"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ଆଡ୍ମିନ୍ ଦ୍ୱାରା ନିୟନ୍ତ୍ରିତ"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ ଦ୍ୱାରା ନିୟନ୍ତ୍ରଣ କରାଯାଇଛି"</string> <string name="disabled" msgid="8017887509554714950">"ଅକ୍ଷମ ହୋଇଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 47c0f8abdbb5..d25b9217184b 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"ਇਮੇਜਿੰਗ"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"ਹੈੱਡਫੋਨ"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ਇਨਪੁੱਟ ਪੈਰਿਫੈਰਲ"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"ਬਲੂਟੁੱਥ"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi ਬੰਦ।"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi ਡਿਸਕਨੈਕਟ ਕੀਤਾ।"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ਰੰਗ ਸੁਧਾਈ ਉਦੋਂ ਲਾਹੇਵੰਦ ਹੋ ਸਕਦੀ ਹੈ, ਜਦੋਂ ਤੁਸੀਂ:<br/> <ol> <li>&nbsp;ਰੰਗਾਂ ਨੂੰ ਹੋਰ ਸਹੀ ਢੰਗ ਨਾਲ ਦੇਖਣਾ ਚਾਹੋ</li> <li>&nbsp;ਫੋਕਸ ਕਰਨ ਵਿੱਚ ਮਦਦ ਲਈ ਰੰਗ ਹਟਾਉਣਾ ਚਾਹੋ</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਐਕਸੈਸਰੀ ਦੀ ਜਾਂਚ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"ਕਨੈਕਟ ਹੋ ਗਿਆ, ਪਰ ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ਚਾਰਜ ਹੋ ਗਈ"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"ਪੂਰੀ ਚਾਰਜ ਹੋ ਗਈ ਹੈ"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗ ਰਾਹੀਂ ਕੰਟਰੋਲ ਕੀਤੀ ਜਾਂਦੀ ਹੈ"</string> <string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index a8ba18d59ab2..72a1b2c6e46f 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Obrazowanie"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Słuchawki"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Peryferyjne urządzenie wejściowe"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Aparaty słuchowe"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi wyłączone."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi odłączone."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korekcja kolorów może być pomocna, gdy:<br/> <ol> <li>&nbsp;chcesz wyraźniej widzieć kolory;</li> <li>&nbsp;chcesz usunąć kolory, aby łatwiej było się skupić.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – wstrzymano ładowanie, aby chronić baterię"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – sprawdzam akcesoria do ładowania"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Jeszcze około <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (na podstawie Twojego sposobu korzystania)"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ładowanie bezprzewodowe"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Ładowanie"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nie podłączony"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Podłączono, ale nie ładuje się"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Naładowana"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Bateria w pełni naładowana"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Ładowanie wstrzymane"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolowane przez administratora"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolowane przez ograniczone ustawienia"</string> <string name="disabled" msgid="8017887509554714950">"Wyłączone"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index fef4378d5b0f..def9d8953839 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Dispozitiv pentru imagini"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Căști"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Dispozitiv periferic de intrare"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Aparate auditive"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi dezactivat."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi deconectat."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corecția culorii poate fi utilă dacă vrei:<br/> <ol> <li>&nbsp;să vezi mai precis culorile;</li> <li>&nbsp;să elimini culorile pentru a te concentra mai bine.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcarea s-a întrerupt pentru a proteja bateria"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Se verifică accesoriul de încărcare"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"În baza utilizării, timpul rămas este de aproximativ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Se încarcă wireless"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Se încarcă"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nu se încarcă"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Conectat, dar nu se încarcă"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Încărcată"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Complet încărcată"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Încărcare întreruptă"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlată de administrator"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlată de setarea restricționată"</string> <string name="disabled" msgid="8017887509554714950">"Dezactivată"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 5989e2a1a392..5cc1a5b3b863 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Камера"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Наушники"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Периферийное устройство ввода"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Слуховые аппараты"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi выключен"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi отключен"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Используйте коррекцию цвета, чтобы:<br/> <ol> <li> Добиться нужной цветопередачи.</li> <li> Убрать цвета, которые мешают сосредоточиться.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"Уровень заряда – <xliff:g id="PERCENTAGE">%1$s</xliff:g>. <xliff:g id="TIME_STRING">%2$s</xliff:g>."</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g>, зарядка приостановлена для защиты батареи"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g>, проверяется зарядное устройство"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Заряда (<xliff:g id="LEVEL">%2$s</xliff:g>) хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Заряда хватит примерно на <xliff:g id="TIME_REMAINING">%1$s</xliff:g> при текущем уровне расхода"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Беспроводная зарядка"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Зарядка"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряжается"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Устройство подключено, но не заряжается"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Батарея заряжена"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Батарея заряжена"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Зарядка приостановлена"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролируется администратором"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролируется настройками с ограниченным доступом"</string> <string name="disabled" msgid="8017887509554714950">"Отключено"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 5c3243b401d9..0d9ca372b64a 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"නිරූපණය"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"හෙඩ්ෆෝන්"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ආදාන උපාංග"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"ශ්රවණාධාරක"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"බ්ලූටූත්"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi අක්රියයි."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wifi සම්බන්ධ කර නොමැත."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"ඔබට පහත දේවල් සිදු කිරීම අවශ්ය විට වර්ණ නිවැරදි කිරීම ප්රයෝජනවත් විය හැකිය:<br/> <ol> <li>වඩාත් නිවැරදිව වර්ණ දැකීම</li> <li>ඔබට අවධානය යොමු කිරීමට උදවු වීමට වර්ණ ඉවත් කිරීම</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය ආරක්ෂා කිරීම සඳහා ආරෝපණය රඳවා තබා ඇත"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණ ආයිත්තම පරීක්ෂා කිරීම"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ක් පමණ ඉතිරියි"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ක් පමණ ඉතිරියි (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"ඔබේ භාවිතය මත පදනම්ව <xliff:g id="TIME_REMAINING">%1$s</xliff:g> පමණ ඉතිරිව ඇත"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"නොරැහැන්ව ආරෝපණය වේ"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"ආරෝපණය වේ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ආරෝපණය නොවේ"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"සම්බන්ධයි, නමුත් ආරෝපණය නොවේ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"අරෝපිතයි"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"සම්පූර්ණයෙන් ආරෝපණ වී ඇත"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"ආරෝපණය රදවාගෙන ඇත"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"පරිපාලක විසින් පාලනය කරන ලදී"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"සීමා කළ සැකසීම මගින් පාලනය වේ"</string> <string name="disabled" msgid="8017887509554714950">"අබල කර ඇත"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index d9fe7381e8b3..a722ad15aec3 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Imazhe"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Kufje"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Hyrje periferike"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Aparatet e dëgjimit"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth-i"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi është çaktivizuar."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi është i shkëputur."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Korrigjimi i ngjyrës mund të jetë i dobishëm kur dëshiron:<br/> <ol> <li>&nbsp;Të shohësh ngjyrat më saktë</li> <li>&nbsp;Të heqësh ngjyrat për të të ndihmuar të fokusohesh</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi është vendosur në pritje për të mbrojtur baterinë"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Po kontrollohet aksesori i karikimit"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura bazuar në përdorimin tënd"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Po karikohet wireless"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Po karikohet"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Është lidhur, por nuk po karikohet"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Karikuar"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Karikuar plotësisht"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Karikimi në pritje"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolluar nga administratori"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollohet nga \"Cilësimet e kufizuara\""</string> <string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 042fc34b0791..66e448bc0aaa 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Обрада слика"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Слушалице"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Периферни уређај за унос"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Слушни апарати"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"WiFi је искључен."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"WiFi веза је прекинута."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Корекција боја може да буде корисна када желите:<br/> <ol> <li>&nbsp;Прецизније да видите боје</li> <li>&nbsp;Да уклоните боје како бисте се фокусирали</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g>–<xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је на чекању да би се заштитила батерија"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – провера додатне опреме за пуњење"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Преостало је око <xliff:g id="TIME_REMAINING">%1$s</xliff:g> на основу коришћења"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бежично пуњење"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Пуњење"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не пуни се"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Уређај је повезан, али се не пуни"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Напуњено"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Напуњено до краја"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Пуњење је на чекању"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Контролише администратор"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролишу ограничена подешавања"</string> <string name="disabled" msgid="8017887509554714950">"Онемогућено"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 1f103e8adeaf..82567bfe6c45 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Bild"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Hörlur"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Inmatning från kringutrustning"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Hörapparater"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi är inaktiverat."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Ingen wifi-anslutning."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Färgkorrigering kan vara bra för att<br/> <ol> <li>&nbsp;urskilja färger bättre</li> <li>&nbsp;ta bort färger som distraherar</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har pausats för att skydda batteriet"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kontrollerar laddningstillbehöret"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Cirka <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kvar utifrån din användning"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laddas trådlöst"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Laddas"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Ansluten men laddas inte"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Laddat"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Fulladdad"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Laddningen har pausats"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Strys av administratören"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styrs av spärrad inställning"</string> <string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 21e7bf3b9fd6..a5ce169a08a3 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"இமேஜிங்"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"ஹெட்ஃபோன்"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"இன்புட் பெரிபெரல்"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"செவித்துணைக் கருவிகள்"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"புளூடூத்"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"வைஃபை முடக்கப்பட்டது."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"வைஃபை துண்டிக்கப்பட்டது."</string> @@ -180,7 +179,7 @@ <string name="running_process_item_user_label" msgid="3988506293099805796">"பயனர்: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"சில இயல்புநிலைகள் அமைக்கப்பட்டன"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"இயல்புநிலைகள் எதுவும் அமைக்கப்படவில்லை"</string> - <string name="tts_settings" msgid="8130616705989351312">"உரை வடிவத்திலிருந்து பேச்சுக்கான அமைப்பு"</string> + <string name="tts_settings" msgid="8130616705989351312">"\'எழுத்திலிருந்து பேச்சு\' அமைப்புகள்"</string> <string name="tts_settings_title" msgid="7602210956640483039">"உரையிலிருந்து பேச்சாக மாற்றுதல்"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"பேச்சு வீதம்"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"பேசப்படும் உரையின் வேகம்"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"நீங்கள் இவற்றைச் செய்ய விரும்பும்போது கலர் கரெக்ஷன் உதவும்:<br/> <ol> <li>&nbsp;வண்ணங்களை மிகத் துல்லியமாகப் பார்த்தல்</li> <li>&nbsp;கவனம் செலுத்துவதற்கு உதவ வண்ணங்களை அகற்றுதல்</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - பேட்டரியைப் பாதுகாப்பதற்காகச் சார்ஜிங் இடைநிறுத்தப்பட்டுள்ளது"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் துணைக்கருவியைச் சரிபார்க்கிறது"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"உபயோகத்தின் அடிப்படையில் கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"வயரின்றி சார்ஜாகிறது"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"சார்ஜாகிறது"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"சார்ஜ் செய்யப்படவில்லை"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"இணைக்கப்பட்டது. ஆனால் சார்ஜ் ஆகவில்லை"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"சார்ஜாகிவிட்டது"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"முழுவதும் சார்ஜாகிவிட்டது"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"சார்ஜிங் இடைநிறுத்தப்பட்டுள்ளது"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"நிர்வாகி கட்டுப்படுத்துகிறார்"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"வரையறுக்கப்பட்ட அமைப்பால் கட்டுப்படுத்தப்படுகிறது"</string> <string name="disabled" msgid="8017887509554714950">"முடக்கப்பட்டது"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index f32a14a49dd9..53f75cbb5198 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Imaging"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Headphone"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Input Peripheral"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Mga Hearing Aid"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Naka-off ang Wifi."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Nakadiskonekta ang Wifi."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Puwedeng makatulong ang pagtatama ng kulay kapag gusto mong:<br/> <ol> <li>&nbsp;Makita nang mas tumpak ang mga kulay</li> <li>&nbsp;Alisin ang mga kulay para matulungan kang tumuon</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-hold ang pag-charge para protektahan ang baterya"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Sinusuri ang accessory sa pag-charge"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Humigit-kumulang <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ang natitira batay sa iyong paggamit"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Wireless na nagcha-charge"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Nagcha-charge"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Hindi nagcha-charge"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Nakakonekta, pero hindi nagcha-charge"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Puno ang Baterya"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Naka-hold ang pag-charge"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pinapamahalaan ng admin"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kinokontrol ng Pinaghihigpitang Setting"</string> <string name="disabled" msgid="8017887509554714950">"Naka-disable"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 06e97291bf60..8e491a16bc07 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Görüntüleme"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Kulaklık"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Giriş Çevre Birimi"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"İşitme Cihazları"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Kablosuz kapalı."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Kablosuz bağlantı kesildi."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Renk düzeltme aşağıdaki durumlarda faydalı olabilir:<br/> <ol> <li>&nbsp;Renkleri daha doğru görmek istediğinizde</li> <li>&nbsp;Odaklanmak için renkleri kaldırmak istediğinizde</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pili korumak için şarj beklemede"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj aksesuarı kontrol ediliyor"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Kullanımınıza dayalı olarak yaklaşık <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kablosuz şarj oluyor"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Şarj Etme"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Şarj olmuyor"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Bağlı ancak şarj olmuyor"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Şarj oldu"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Pilin Şarjı Tam"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Şarj işlemi beklemede"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Yönetici tarafından denetleniyor"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kısıtlanmış ayar tarafından kontrol ediliyor"</string> <string name="disabled" msgid="8017887509554714950">"Devre dışı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index cd00e172d5cc..45ed50878cf5 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Візуалізація"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Навушники"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Периферійне введення"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Слухові апарати"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi вимкнено."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi від’єднано."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Корекція кольору корисна, якщо ви хочете:<br/> <ol> <li>&nbsp;точніше бачити кольори;</li> <li>&nbsp;вилучити кольори, щоб легше зосереджуватися.</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання призупинено, щоб захистити акумулятор"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – перевірка зарядного пристрою"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Згідно з даними про використання залишилося приблизно <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бездротове заряджання"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Заряджання"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряджається"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Пристрій підключено, але він не заряджається"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Заряджено"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Повністю заряджено"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Заряджання призупинено"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Керується адміністратором"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Керується налаштуваннями з обмеженнями"</string> <string name="disabled" msgid="8017887509554714950">"Вимкнено"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 3acb9cd422ac..b46fca57d150 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Kamera"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Quloqchin"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Kiritish qurilmasi"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Eshitish moslamalari"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi o‘chiq."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi o‘chiq."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Ranglarni tuzatishning foydasi:<br/> <ol> <li>&nbsp;Ranglar yanada aniqroq koʻrinadi</li> <li>&nbsp;Diqqatni qaratish uchun ortiqcha ranglarni olib tashlash imkonini beradi</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyani himoyalash uchun quvvatlash toʻxtatildi"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash aksessuari tekshirilmoqda"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Quvvati tugashiga taxminan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qoldi"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz quvvat olmoqda"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Quvvat olmoqda"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Quvvat olmayapti"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Ulangan, lekin quvvat olmayapti"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Quvvat oldi"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Toʻliq quvvatlandi"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Quvvatlash toʻxtatildi"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administrator tomonidan boshqariladi"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Cheklangan sozlama tomonidan boshqariladi"</string> <string name="disabled" msgid="8017887509554714950">"Oʻchiq"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index fcc049ddcdae..c471546eabc2 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Tạo ảnh"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Tai nghe"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Thiết bị ngoại vi vào"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"Thiết bị trợ thính"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Đã tắt Wi-Fi."</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Đã ngắt kết nối Wi-Fi."</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Tính năng chỉnh màu có thể giúp ích khi bạn muốn:<br/> <ol> <li>&nbsp;Thấy màu sắc chính xác hơn</li> <li>&nbsp;Loại bỏ bớt màu để tập trung</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang tạm ngưng sạc để bảo vệ pin"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang kiểm tra phụ kiện sạc"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"Còn khoảng <xliff:g id="TIME_REMAINING">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Đang sạc không dây"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Đang sạc"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Hiện không sạc"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Đã kết nối, nhưng không sạc"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Đã sạc"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Đã sạc đầy"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Đang tạm ngưng sạc"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Do quản trị viên kiểm soát"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Do chế độ Cài đặt hạn chế kiểm soát"</string> <string name="disabled" msgid="8017887509554714950">"Đã tắt"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index babcc07beaf2..e9774e11617f 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -456,7 +456,7 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"“色彩校正”功能适用于以下情况:<br/> <ol> <li>您想更准确地查看颜色</li> <li>您想移除颜色以提高专注程度</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 为保护电池,充电已暂停"</string> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 为保护电池,已暂停充电"</string> <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在检查充电配件"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -489,7 +489,7 @@ <string name="battery_info_status_not_charging" msgid="1103084691314264664">"已连接,但未充电"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"已充满电"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"已充满电"</string> - <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"目前暂停充电"</string> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"充电已暂停"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"由管理员控制"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限设置控制"</string> <string name="disabled" msgid="8017887509554714950">"已停用"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 390ae37d7e85..1e3394d82261 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"映像設備"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"耳機"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"輸入周邊設備"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"助聽器"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"藍牙"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi 已關閉。"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi 連線已中斷。"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"「色彩校正」功能適用於以下情況::<br/> <ol> <li>&nbsp;你想讓裝置顯示更準確的色彩</li> <li>&nbsp;你想移除色彩以提高專注力</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在檢查充電配件"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根據你的使用情況,還有大約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"無線充電中"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"正在充電"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"已連接,但未充電"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"已充滿電"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"充電完成"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"目前暫停充電"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"已由管理員停用"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由「受限設定」控制"</string> <string name="disabled" msgid="8017887509554714950">"已停用"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 5f4b9ed77bf4..8643fde22819 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -154,8 +154,7 @@ <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"顯像裝置"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"頭戴式耳機"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"周邊輸入裝置"</string> - <!-- no translation found for bluetooth_talkback_hearing_aids (3983279945542595479) --> - <skip /> + <string name="bluetooth_talkback_hearing_aids" msgid="3983279945542595479">"助聽器"</string> <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"藍牙"</string> <string name="accessibility_wifi_off" msgid="1195445715254137155">"已關閉 Wi-Fi。"</string> <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi 連線已中斷。"</string> @@ -457,10 +456,8 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"「色彩校正」功能適用於以下情況:<br/> <ol> <li>&nbsp;你想讓裝置顯示更準確的色彩</li> <li>&nbsp;你想移除色彩以提升專注力</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_on_hold_settings_home_page (7690464049464805856) --> - <skip /> - <!-- no translation found for power_incompatible_charging_settings_home_page (1261756225093962684) --> - <skip /> + <string name="power_charging_on_hold_settings_home_page" msgid="7690464049464805856">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string> + <string name="power_incompatible_charging_settings_home_page" msgid="1261756225093962684">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在檢查充電配件"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_discharging_duration" msgid="1076561255466053220">"目前電量為 <xliff:g id="LEVEL">%2$s</xliff:g>,還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根據你的使用情形,還能使用約 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> @@ -489,12 +486,10 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在進行無線充電"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"充電中"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string> - <!-- no translation found for battery_info_status_not_charging (1103084691314264664) --> - <skip /> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"已連接,但未充電"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"充電完成"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"充電完成"</string> - <!-- no translation found for battery_info_status_charging_on_hold (6364355145521694438) --> - <skip /> + <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"目前暫停充電"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"已由管理員停用"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由限制設定控管"</string> <string name="disabled" msgid="8017887509554714950">"已停用"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt new file mode 100644 index 000000000000..3355fb395ca0 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt @@ -0,0 +1,53 @@ +/* + * 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.volume.data.repository + +import android.media.AudioManager +import com.android.internal.util.ConcurrentUtils +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +/** Provides audio managing functionality and data. */ +interface AudioRepository { + + /** Current [AudioManager.getMode]. */ + val mode: StateFlow<Int> +} + +class AudioRepositoryImpl( + private val audioManager: AudioManager, + backgroundCoroutineContext: CoroutineContext, + coroutineScope: CoroutineScope, +) : AudioRepository { + + override val mode: StateFlow<Int> = + callbackFlow { + val listener = + AudioManager.OnModeChangedListener { newMode -> launch { send(newMode) } } + audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener) + awaitClose { audioManager.removeOnModeChangedListener(listener) } + } + .flowOn(backgroundCoroutineContext) + .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode) +} diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt new file mode 100644 index 000000000000..053c59b15067 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.volume.domain.interactor + +import android.media.AudioManager +import com.android.settingslib.volume.data.repository.AudioRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class AudioModeInteractor(repository: AudioRepository) { + + private val ongoingCallModes = + setOf( + AudioManager.MODE_RINGTONE, + AudioManager.MODE_IN_CALL, + AudioManager.MODE_IN_COMMUNICATION, + ) + + /** Returns if current [AudioManager.getMode] call is an ongoing call */ + val isOngoingCall: Flow<Boolean> = repository.mode.map { it in ongoingCallModes } +} diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index 644b72c383ac..ce3a7baf6be6 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -57,6 +57,7 @@ android_test { "SettingsLibSettingsSpinner", "SettingsLibUsageProgressBarPreference", "settingslib_media_flags_lib", + "kotlinx_coroutines_test", ], dxflags: ["--multi-dex"], diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt new file mode 100644 index 000000000000..686362fadf02 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.volume.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeAudioRepository : AudioRepository { + + private val mutableMode = MutableStateFlow(0) + override val mode: StateFlow<Int> + get() = mutableMode.asStateFlow() + + fun setMode(newMode: Int) { + mutableMode.value = newMode + } +} diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt new file mode 100644 index 000000000000..3bc1edc9c944 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt @@ -0,0 +1,79 @@ +/* + * 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.volume.domain.interactor + +import android.media.AudioManager +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.settingslib.volume.data.repository.FakeAudioRepository +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +@SmallTest +class AudioModeInteractorTest { + + private val testScope = TestScope() + private val fakeAudioRepository = FakeAudioRepository() + + private val underTest = AudioModeInteractor(fakeAudioRepository) + + @Test + fun ongoingCallModes_isOnGoingCall() { + testScope.runTest { + for (mode in ongoingCallModes) { + var isOngoingCall = false + underTest.isOngoingCall.onEach { isOngoingCall = it }.launchIn(backgroundScope) + + fakeAudioRepository.setMode(mode) + runCurrent() + + assertThat(isOngoingCall).isTrue() + } + } + } + + @Test + fun notOngoingCallModes_isNotOnGoingCall() { + testScope.runTest { + var isOngoingCall = true + underTest.isOngoingCall.onEach { isOngoingCall = it }.launchIn(backgroundScope) + + fakeAudioRepository.setMode(AudioManager.MODE_CURRENT) + runCurrent() + + assertThat(isOngoingCall).isFalse() + } + } + + private companion object { + private val ongoingCallModes = + setOf( + AudioManager.MODE_RINGTONE, + AudioManager.MODE_IN_CALL, + AudioManager.MODE_IN_COMMUNICATION, + ) + } +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index cdb4aea6ee79..95e0e1b0fa09 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -899,6 +899,9 @@ <!-- Permission required for Cts test - CtsNotificationTestCases --> <uses-permission android:name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" /> + <!-- Permission required for Cts test - CtsWindowManagerJetpackTestCases --> + <uses-permission android:name="android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE" /> + <!-- Permission required for BinaryTransparencyService shell API and host test --> <uses-permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" /> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 80656e9253db..900a2f830783 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -214,6 +214,8 @@ android_library { javacflags: [ "-Adagger.fastInit=enabled", + "-Adagger.explicitBindingConflictsWithInject=ERROR", + "-Adagger.strictMultibindingValidation=enabled", "-Aroom.schemaLocation=frameworks/base/packages/SystemUI/schemas", ], kotlincflags: ["-Xjvm-default=all"], @@ -330,6 +332,7 @@ android_library { "androidx.core_core-animation-testing-nodeps", "androidx.compose.ui_ui", "flag-junit", + "ravenwood-junit", "platform-test-annotations", "notification_flags_lib", ], @@ -386,7 +389,8 @@ android_library { android_app { name: "SystemUIRobo-stub", - use_resource_processor: true, + // SystemUiRavenTests references the .aapt.srcjar + use_resource_processor: false, defaults: [ "platform_app_defaults", "SystemUI_optimized_defaults", @@ -458,6 +462,34 @@ android_robolectric_test { ], } +android_ravenwood_test { + name: "SystemUiRavenTests", + srcs: [ + ":SystemUI-tests-utils", + ":SystemUI-tests-multivalent", + // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable + // use_resource_processor: true when better supported by soong + ":SystemUIRobo-stub{.aapt.srcjar}", + ], + static_libs: [ + "SystemUI-core", + "SystemUI-res", + "SystemUI-tests-base", + "androidx.test.uiautomator_uiautomator", + "androidx.core_core-animation-testing", + "androidx.test.ext.junit", + ], + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], + auto_gen_config: true, + plugins: [ + "dagger2-compiler", + ], +} + // Opt-out config for optimizing the SystemUI target using R8. // Disabled via `export SYSTEMUI_OPTIMIZE_JAVA=false`, or explicitly in Make via // `SYSTEMUI_OPTIMIZE_JAVA := false`. diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig index 5fd3b485e9ed..7cc0c83abfea 100644 --- a/packages/SystemUI/aconfig/biometrics_framework.aconfig +++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig @@ -7,4 +7,11 @@ flag { namespace: "biometrics_framework" description: "Adds talkback directional guidance when using UDFPS with biometric prompt" bug: "310044658" +} + +flag { + name: "constraint_bp" + namespace: "biometrics_framework" + description: "Refactors Biometric Prompt to use a ConstraintLayout" + bug: "288175072" }
\ No newline at end of file diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a2530d59e2e6..0ea2b1fed968 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -59,14 +59,6 @@ flag { } flag { - name: "notification_lifetime_extension_refactor" - namespace: "systemui" - description: "Enables moving notification lifetime extension management from SystemUI to " - "Notification Manager Service" - bug: "299448097" -} - -flag { name: "notifications_live_data_store_refactor" namespace: "systemui" description: "Replaces NotifLiveDataStore with ActiveNotificationListRepository, and updates consumers. " @@ -371,3 +363,10 @@ flag { description: "Enables refactored logic for SysUI+WM unlock/occlusion code paths" bug: "278086361" } + +flag { + name: "enable_keyguard_compose" + namespace: "systemui" + description: "Enables the compose version of keyguard." + bug: "301968149" +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 9c46ebdc5ac8..81940553b127 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -644,7 +644,7 @@ class ActivityLaunchAnimator( var candidate: RemoteAnimationTarget? = null for (it in apps) { if (it.mode == RemoteAnimationTarget.MODE_OPENING) { - if (it.taskInfo != null && !it.hasAnimatingParent) { + if (!it.hasAnimatingParent) { return it } if (candidate == null) { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt index bb2fbf73b2e5..a18b460cb168 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt @@ -17,6 +17,7 @@ package com.android.compose +import androidx.annotation.DrawableRes import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope @@ -24,8 +25,13 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonColors +import androidx.compose.material3.IconButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.theme.LocalAndroidColorScheme @@ -89,6 +95,29 @@ fun PlatformTextButton( ) } +@Composable +fun PlatformIconButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + colors: IconButtonColors = iconButtonColors(), + @DrawableRes iconResource: Int, + contentDescription: String?, +) { + IconButton( + modifier = modifier, + onClick = onClick, + enabled = enabled, + colors = colors, + ) { + Icon( + painter = painterResource(id = iconResource), + contentDescription = contentDescription, + tint = colors.contentColor, + ) + } +} + private val DefaultPlatformButtonVerticalPadding = 6.dp private val ButtonPaddings = PaddingValues(horizontal = 16.dp, vertical = 8.dp) @@ -109,6 +138,13 @@ private fun outlineButtonColors(): ButtonColors { } @Composable +private fun iconButtonColors(): IconButtonColors { + return IconButtonDefaults.filledIconButtonColors( + contentColor = LocalAndroidColorScheme.current.onSurface, + ) +} + +@Composable private fun outlineButtonBorder(): BorderStroke { return BorderStroke( width = 1.dp, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt index 09608115c456..f9b91cf4c10b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt @@ -14,9 +14,10 @@ * limitations under the License. */ +@file:OptIn(ExperimentalComposeUiApi::class) + package com.android.systemui.bouncer.ui.composable -import android.view.ViewTreeObserver import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.LocalTextStyle @@ -25,25 +26,25 @@ import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onInterceptKeyBeforeSoftKeyboard import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.core.view.WindowInsetsCompat import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel /** UI for the input part of a password-requiring version of the bouncer. */ @@ -64,9 +65,6 @@ internal fun PasswordBouncer( val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState() val animateFailure: Boolean by viewModel.animateFailure.collectAsState() - val isImeVisible by isSoftwareKeyboardVisible() - LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) } - DisposableEffect(Unit) { viewModel.onShown() onDispose { viewModel.onHidden() } @@ -109,27 +107,14 @@ internal fun PasswordBouncer( end = Offset(size.width, y = size.height - lineWidthPx), strokeWidth = lineWidthPx, ) + } + .onInterceptKeyBeforeSoftKeyboard { keyEvent -> + if (keyEvent.key == Key.Back) { + viewModel.onImeDismissed() + true + } else { + false + } }, ) } - -/** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */ -@Composable -fun isSoftwareKeyboardVisible(): State<Boolean> { - val view = LocalView.current - val viewTreeObserver = view.viewTreeObserver - - return produceState( - initialValue = false, - key1 = viewTreeObserver, - ) { - val listener = - ViewTreeObserver.OnGlobalLayoutListener { - value = view.rootWindowInsets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false - } - - viewTreeObserver.addOnGlobalLayoutListener(listener) - - awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener) } - } -} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index 4fdcf7579dc6..92bc1f18a6c5 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -64,14 +64,6 @@ fun CommunalContainer( transitions = sceneTransitions, ) - // Don't show hub mode UI if communal is not available. Communal is only available if it has - // been enabled via settings and either keyguard is showing, or, the device is currently - // dreaming. - val isCommunalAvailable by viewModel.isCommunalAvailable.collectAsState() - if (!isCommunalAvailable) { - return - } - // This effect exposes the SceneTransitionLayout's observable transition state to the rest of // the system, and unsets it when the view is disposed to avoid a memory leak. DisposableEffect(viewModel, sceneTransitionLayoutState) { @@ -132,7 +124,7 @@ fun SceneKey.toCommunalSceneKey(): CommunalSceneKey { // TODO(b/315490861): Remove these conversions once Compose can be used throughout SysUI. fun CommunalSceneKey.toTransitionSceneKey(): SceneKey { - return SceneKey(name = toString(), identity = this) + return SceneKey(debugName = toString(), identity = this) } /** 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 761e74e52237..576596fa67fe 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 @@ -48,7 +48,6 @@ 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 import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.TouchApp @@ -60,7 +59,6 @@ import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton @@ -130,12 +128,11 @@ fun CommunalHub( val gridState = rememberLazyGridState() val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel) val reorderingWidgets by viewModel.reorderingWidgets.collectAsState() - val selectedIndex = viewModel.selectedIndex.collectAsState() + val selectedKey = viewModel.selectedKey.collectAsState() val removeButtonEnabled by remember { - derivedStateOf { selectedIndex.value != null || reorderingWidgets } + derivedStateOf { selectedKey.value != null || reorderingWidgets } } - val (isButtonToEditWidgetsShowing, setIsButtonToEditWidgetsShowing) = - remember { mutableStateOf(false) } + var isButtonToEditWidgetsShowing by remember { mutableStateOf(false) } val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize) val contentOffset = beforeContentPadding(contentPadding).toOffset() @@ -150,22 +147,30 @@ fun CommunalHub( if (!viewModel.isEditMode) return@pointerInput observeTapsWithoutConsuming { offset -> val adjustedOffset = offset - contentOffset - val index = - gridState.layoutInfo.visibleItemsInfo - .firstItemAtOffset(adjustedOffset) - ?.index - val newIndex = - if (index?.let(contentListState::isItemEditable) == true) { - index - } else { - null - } - viewModel.setSelectedIndex(newIndex) + val index = firstIndexAtOffset(gridState, adjustedOffset) + val key = index?.let { keyAtIndexIfEditable(contentListState.list, index) } + viewModel.setSelectedKey(key) } } .thenIf(!viewModel.isEditMode) { - Modifier.pointerInput(Unit) { - detectLongPressGesture { offset -> setIsButtonToEditWidgetsShowing(true) } + Modifier.pointerInput( + gridState, + contentOffset, + communalContent, + gridCoordinates + ) { + detectLongPressGesture { offset -> + isButtonToEditWidgetsShowing = true + + // Deduct both grid offset relative to its container and content offset. + val adjustedOffset = + gridCoordinates?.let { + offset - it.positionInWindow() - contentOffset + } + val index = adjustedOffset?.let { firstIndexAtOffset(gridState, it) } + val key = index?.let { keyAtIndexIfEditable(communalContent, index) } + viewModel.setSelectedKey(key) + } } }, ) { @@ -186,7 +191,7 @@ fun CommunalHub( onOpenWidgetPicker = onOpenWidgetPicker, gridState = gridState, contentListState = contentListState, - selectedIndex = selectedIndex, + selectedKey = selectedKey, widgetConfigurator = widgetConfigurator, ) @@ -198,18 +203,18 @@ fun CommunalHub( onEditDone = onEditDone, onOpenWidgetPicker = onOpenWidgetPicker, onRemoveClicked = { - selectedIndex.value?.let { index -> - contentListState.onRemove(index) + val index = + selectedKey.value?.let { key -> + contentListState.list.indexOfFirst { it.key == key } + } + index?.let { + contentListState.onRemove(it) contentListState.onSaveList() - viewModel.setSelectedIndex(null) + viewModel.setSelectedKey(null) } }, removeEnabled = removeButtonEnabled ) - } else { - IconButton(onClick = viewModel::onOpenWidgetEditor) { - Icon(Icons.Default.Edit, stringResource(R.string.button_to_open_widget_editor)) - } } if (isPopupOnDismissCtaShowing) { @@ -219,10 +224,10 @@ fun CommunalHub( if (isButtonToEditWidgetsShowing) { ButtonToEditWidgets( onClick = { - setIsButtonToEditWidgetsShowing(false) - viewModel.onOpenWidgetEditor() + isButtonToEditWidgetsShowing = false + viewModel.onOpenWidgetEditor(selectedKey.value) }, - onHide = { setIsButtonToEditWidgetsShowing(false) }, + onHide = { isButtonToEditWidgetsShowing = false }, ) } @@ -244,7 +249,7 @@ private fun BoxScope.CommunalHubLazyGrid( communalContent: List<CommunalContentModel>, viewModel: BaseCommunalViewModel, contentPadding: PaddingValues, - selectedIndex: State<Int?>, + selectedKey: State<String?>, contentOffset: Offset, gridState: LazyGridState, contentListState: ContentListState, @@ -253,7 +258,8 @@ private fun BoxScope.CommunalHubLazyGrid( onOpenWidgetPicker: (() -> Unit)? = null, widgetConfigurator: WidgetConfigurator?, ) { - var gridModifier = Modifier.align(Alignment.CenterStart) + var gridModifier = + Modifier.align(Alignment.CenterStart).onGloballyPositioned { setGridCoordinates(it) } var list = communalContent var dragDropState: GridDragDropState? = null if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) { @@ -266,10 +272,7 @@ private fun BoxScope.CommunalHubLazyGrid( updateDragPositionForRemove = updateDragPositionForRemove ) gridModifier = - gridModifier - .fillMaxSize() - .dragContainer(dragDropState, contentOffset, viewModel) - .onGloballyPositioned { setGridCoordinates(it) } + gridModifier.fillMaxSize().dragContainer(dragDropState, contentOffset, viewModel) // for widgets dropped from other activities val dragAndDropTargetState = rememberDragAndDropTargetState( @@ -307,7 +310,8 @@ private fun BoxScope.CommunalHubLazyGrid( list[index].size.dp().value, ) if (viewModel.isEditMode && dragDropState != null) { - val selected by remember(index) { derivedStateOf { index == selectedIndex.value } } + val selected by + remember(index) { derivedStateOf { list[index].key == selectedKey.value } } DraggableItem( dragDropState = dragDropState, selected = selected, @@ -378,7 +382,7 @@ private fun Toolbar( colors = filledButtonColors(), contentPadding = Dimensions.ButtonPadding ) { - Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor)) + Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) Spacer(spacerModifier) Text( text = stringResource(R.string.hub_mode_add_widget_button_text), @@ -832,6 +836,13 @@ private fun CommunalContentSize.dp(): Dp { } } +private fun firstIndexAtOffset(gridState: LazyGridState, offset: Offset): Int? = + gridState.layoutInfo.visibleItemsInfo.firstItemAtOffset(offset)?.index + +/** Returns the key of item if it's editable at the given index. Only widget is editable. */ +private fun keyAtIndexIfEditable(list: List<CommunalContentModel>, index: Int): String? = + if (index in list.indices && list[index].isWidget()) list[index].key else null + data class ContentPaddingInPx(val start: Float, val top: Float) { fun toOffset(): Offset = Offset(start, top) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt index 42fcd1363f11..5e27d8299c16 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt @@ -21,19 +21,27 @@ import android.view.ViewGroup import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.android.compose.animation.scene.SceneScope +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.notifications.ui.composable.NotificationStack import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationStackAppearanceViewBinder +import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +@SysUISingleton class NotificationSection @Inject constructor( @@ -42,22 +50,41 @@ constructor( controller: NotificationStackScrollLayoutController, sceneContainerFlags: SceneContainerFlags, sharedNotificationContainer: SharedNotificationContainer, + sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, stackScrollLayout: NotificationStackScrollLayout, notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel, ambientState: AmbientState, + notificationStackSizeCalculator: NotificationStackSizeCalculator, + @Main mainDispatcher: CoroutineDispatcher, ) { init { - if (sceneContainerFlags.flexiNotifsEnabled()) { + if (!KeyguardShadeMigrationNssl.isUnexpectedlyInLegacyMode()) { + // This scene container section moves the NSSL to the SharedNotificationContainer. This + // also requires that SharedNotificationContainer gets moved to the SceneWindowRootView + // by the SceneWindowRootViewBinder. + // Prior to Scene Container, but when the KeyguardShadeMigrationNssl flag is enabled, + // NSSL is moved into this container by the NotificationStackScrollLayoutSection. (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout) sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout) - NotificationStackAppearanceViewBinder.bind( - context, + SharedNotificationContainerBinder.bind( sharedNotificationContainer, - notificationStackAppearanceViewModel, - ambientState, + sharedNotificationContainerViewModel, + sceneContainerFlags, controller, + notificationStackSizeCalculator, + mainDispatcher, ) + + if (sceneContainerFlags.flexiNotifsEnabled()) { + NotificationStackAppearanceViewBinder.bind( + context, + sharedNotificationContainer, + notificationStackAppearanceViewModel, + ambientState, + controller, + ) + } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt index 5336bf646231..0c66701de61c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/TransitionSceneKeys.kt @@ -12,5 +12,5 @@ val Communal = SceneKey.Communal.toTransitionSceneKey() // TODO(b/293899074): Remove this file once we can use the scene keys from SceneTransitionLayout. fun SceneKey.toTransitionSceneKey(): SceneTransitionSceneKey { - return SceneTransitionSceneKey(name = toString(), identity = this) + return SceneTransitionSceneKey(debugName = toString(), identity = this) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index e2beaeea6402..b11edf7b47b7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -58,6 +58,8 @@ import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout import com.android.systemui.res.R +import com.android.systemui.scene.ui.composable.QuickSettings +import com.android.systemui.scene.ui.composable.Shade as ShadeKey import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager @@ -348,7 +350,7 @@ private fun ShadeCarrierGroup( } @Composable -private fun StatusIcons( +private fun SceneScope.StatusIcons( viewModel: ShadeHeaderViewModel, createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager, statusBarIconController: StatusBarIconController, @@ -358,7 +360,6 @@ private fun StatusIcons( val carrierIconSlots = listOf(stringResource(id = com.android.internal.R.string.status_bar_mobile)) val isSingleCarrier by viewModel.isSingleCarrier.collectAsState() - val isTransitioning by viewModel.isTransitioning.collectAsState() AndroidView( factory = { context -> @@ -373,7 +374,9 @@ private fun StatusIcons( iconContainer }, update = { iconContainer -> - iconContainer.setQsExpansionTransitioning(isTransitioning) + iconContainer.setQsExpansionTransitioning( + layoutState.isTransitioningBetween(ShadeKey, QuickSettings) + ) if (isSingleCarrier || !useExpandedFormat) { iconContainer.removeIgnoredSlots(carrierIconSlots) } else { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt index 7d3b0fbe1725..705754594903 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -61,7 +61,7 @@ internal fun CoroutineScope.animateToScene( // The transition is already finished (progress ~= 1): no need to animate. We // finish the current transition early to make sure that the current state // change is committed. - layoutState.finishTransition(transitionState, transitionState.currentScene) + layoutState.finishTransition(transitionState, target) null } else { // The transition is in progress: start the canned animation at the same @@ -78,7 +78,7 @@ internal fun CoroutineScope.animateToScene( if (progress.absoluteValue < ProgressVisibilityThreshold) { // The transition is at progress ~= 0: no need to animate.We finish the current // transition early to make sure that the current state change is committed. - layoutState.finishTransition(transitionState, transitionState.currentScene) + layoutState.finishTransition(transitionState, target) null } else { // TODO(b/290184746): Also take the current velocity into account. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt index 9d4b69c51690..c68519547c7e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt @@ -42,16 +42,16 @@ sealed class Key(val debugName: String, val identity: Any) { /** Key for a scene. */ class SceneKey( - name: String, + debugName: String, identity: Any = Object(), -) : Key(name, identity), UserActionResult { +) : Key(debugName, identity), UserActionResult { @VisibleForTesting // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can // access internal members. - val testTag: String = "scene:$name" + val testTag: String = "scene:$debugName" /** The unique [ElementKey] identifying this scene's root element. */ - val rootElementKey = ElementKey(name, identity) + val rootElementKey = ElementKey(debugName, identity) // Implementation of [UserActionResult]. override val toScene: SceneKey = this @@ -64,7 +64,7 @@ class SceneKey( /** Key for an element. */ class ElementKey( - name: String, + debugName: String, identity: Any = Object(), /** @@ -72,11 +72,11 @@ class ElementKey( * or compose MovableElements. */ val scenePicker: ElementScenePicker = DefaultElementScenePicker, -) : Key(name, identity), ElementMatcher { +) : Key(debugName, identity), ElementMatcher { @VisibleForTesting // TODO(b/240432457): Make internal once PlatformComposeSceneTransitionLayoutTestsUtils can // access internal members. - val testTag: String = "element:$name" + val testTag: String = "element:$debugName" override fun matches(key: ElementKey, scene: SceneKey): Boolean { return key == this @@ -99,7 +99,7 @@ class ElementKey( } /** Key for a shared value of an element. */ -class ValueKey(name: String, identity: Any = Object()) : Key(name, identity) { +class ValueKey(debugName: String, identity: Any = Object()) : Key(debugName, identity) { override fun toString(): String { return "ValueKey(debugName=$debugName)" } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index 8552aaf0ebff..529fc0327fcb 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -40,12 +40,15 @@ import androidx.compose.ui.input.pointer.util.addPointerInputChange import androidx.compose.ui.node.CompositionLocalConsumerModifierNode import androidx.compose.ui.node.DelegatingNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.ObserverModifierNode import androidx.compose.ui.node.PointerInputModifierNode import androidx.compose.ui.node.currentValueOf +import androidx.compose.ui.node.observeReads import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity import androidx.compose.ui.util.fastForEach +import kotlin.math.sign /** * Make an element draggable in the given [orientation]. @@ -65,7 +68,7 @@ import androidx.compose.ui.util.fastForEach internal fun Modifier.multiPointerDraggable( orientation: Orientation, enabled: () -> Boolean, - startDragImmediately: () -> Boolean, + startDragImmediately: (startedPosition: Offset) -> Boolean, onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, onDragDelta: (delta: Float) -> Unit, onDragStopped: (velocity: Float) -> Unit, @@ -84,7 +87,7 @@ internal fun Modifier.multiPointerDraggable( private data class MultiPointerDraggableElement( private val orientation: Orientation, private val enabled: () -> Boolean, - private val startDragImmediately: () -> Boolean, + private val startDragImmediately: (startedPosition: Offset) -> Boolean, private val onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, private val onDragDelta: (Float) -> Unit, @@ -113,14 +116,19 @@ private data class MultiPointerDraggableElement( internal class MultiPointerDraggableNode( orientation: Orientation, enabled: () -> Boolean, - var startDragImmediately: () -> Boolean, + var startDragImmediately: (startedPosition: Offset) -> Boolean, var onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, var onDragDelta: (Float) -> Unit, var onDragStopped: (velocity: Float) -> Unit, -) : PointerInputModifierNode, DelegatingNode(), CompositionLocalConsumerModifierNode { +) : + PointerInputModifierNode, + DelegatingNode(), + CompositionLocalConsumerModifierNode, + ObserverModifierNode { private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() } private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler)) private val velocityTracker = VelocityTracker() + private var previousEnabled: Boolean = false var enabled: () -> Boolean = enabled set(value) { @@ -140,6 +148,21 @@ internal class MultiPointerDraggableNode( } } + override fun onAttach() { + previousEnabled = enabled() + onObservedReadsChanged() + } + + override fun onObservedReadsChanged() { + observeReads { + val newEnabled = enabled() + if (newEnabled != previousEnabled) { + delegate.resetPointerInputHandler() + } + previousEnabled = newEnabled + } + } + override fun onCancelPointerInput() = delegate.onCancelPointerInput() override fun onPointerEvent( @@ -201,7 +224,7 @@ internal class MultiPointerDraggableNode( */ private suspend fun PointerInputScope.detectDragGestures( orientation: Orientation, - startDragImmediately: () -> Boolean, + startDragImmediately: (startedPosition: Offset) -> Boolean, onDragStart: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, onDragEnd: () -> Unit, onDragCancel: () -> Unit, @@ -211,7 +234,7 @@ private suspend fun PointerInputScope.detectDragGestures( val initialDown = awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial) var overSlop = 0f val drag = - if (startDragImmediately()) { + if (startDragImmediately(initialDown.position)) { initialDown.consume() initialDown } else { @@ -223,12 +246,31 @@ private suspend fun PointerInputScope.detectDragGestures( // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once // it is public. - when (orientation) { - Orientation.Horizontal -> - awaitHorizontalTouchSlopOrCancellation(down.id, onSlopReached) - Orientation.Vertical -> - awaitVerticalTouchSlopOrCancellation(down.id, onSlopReached) + val drag = + when (orientation) { + Orientation.Horizontal -> + awaitHorizontalTouchSlopOrCancellation(down.id, onSlopReached) + Orientation.Vertical -> + awaitVerticalTouchSlopOrCancellation(down.id, onSlopReached) + } + + // Make sure that overSlop is not 0f. This can happen when the user drags by exactly + // the touch slop. However, the overSlop we pass to onDragStarted() is used to + // compute the direction we are dragging in, so overSlop should never be 0f unless + // we intercept an ongoing swipe transition (i.e. startDragImmediately() returned + // true). + if (drag != null && overSlop == 0f) { + val deltaOffset = drag.position - initialDown.position + val delta = + when (orientation) { + Orientation.Horizontal -> deltaOffset.x + Orientation.Vertical -> deltaOffset.y + } + check(delta != 0f) { "delta is equal to 0" } + overSlop = delta.sign } + + drag } if (drag != null) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index aed04f688628..35754d624ccd 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -20,8 +20,7 @@ package com.android.compose.animation.scene import android.util.Log import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.spring +import androidx.compose.animation.core.SpringSpec import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -54,7 +53,20 @@ internal class SceneGestureHandler( } private fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) { - if (isDrivingTransition || force) layoutState.startTransition(newTransition) + if (isDrivingTransition || force) { + layoutState.startTransition(newTransition) + + // Initialize SwipeTransition.swipeSpec. Note that this must be called right after + // layoutState.startTransition() is called, because it computes the + // layoutState.transformationSpec(). + newTransition.swipeSpec = + layoutState.transformationSpec.swipeSpec ?: layoutState.transitions.defaultSwipeSpec + } else { + // We were not driving the transition and we don't force the update, so the spec won't + // be used and it doesn't matter which one we set here. + newTransition.swipeSpec = SceneTransitions.DefaultSwipeSpec + } + swipeTransition = newTransition } @@ -84,8 +96,35 @@ internal class SceneGestureHandler( private var upOrLeftResult: UserActionResult? = null private var downOrRightResult: UserActionResult? = null + /** + * Whether we should immediately intercept a gesture. + * + * Note: if this returns true, then [onDragStarted] will be called with overSlop equal to 0f, + * indicating that the transition should be intercepted. + */ + internal fun shouldImmediatelyIntercept(startedPosition: Offset?): Boolean { + // We don't intercept the touch if we are not currently driving the transition. + if (!isDrivingTransition) { + return false + } + + // Only intercept the current transition if one of the 2 swipes results is also a transition + // between the same pair of scenes. + val fromScene = swipeTransition._currentScene + val swipes = computeSwipes(fromScene, startedPosition, pointersDown = 1) + val (upOrLeft, downOrRight) = computeSwipesResults(fromScene, swipes) + return (upOrLeft != null && + swipeTransition.isTransitioningBetween(fromScene.key, upOrLeft.toScene)) || + (downOrRight != null && + swipeTransition.isTransitioningBetween(fromScene.key, downOrRight.toScene)) + } + internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?, overSlop: Float) { - if (isDrivingTransition) { + if (overSlop == 0f) { + check(isDrivingTransition) { + "onDragStarted() called while isDrivingTransition=false overSlop=0f" + } + // This [transition] was already driving the animation: simply take over it. // Stop animating and start from where the current offset. swipeTransition.cancelOffsetAnimation() @@ -93,9 +132,6 @@ internal class SceneGestureHandler( return } - check(overSlop != 0f) { - "onDragStarted() called while isDrivingTransition=false overSlop=0f" - } val transitionState = layoutState.transitionState if (transitionState is TransitionState.Transition) { // TODO(b/290184746): Better handle interruptions here if state != idle. @@ -110,9 +146,10 @@ internal class SceneGestureHandler( val fromScene = layoutImpl.scene(transitionState.currentScene) updateSwipes(fromScene, startedPosition, pointersDown) - val (targetScene, distance) = - findTargetSceneAndDistance(fromScene, overSlop, updateSwipesResults = true) ?: return - updateTransition(SwipeTransition(fromScene, targetScene, distance), force = true) + val result = + findUserActionResult(fromScene, directionOffset = overSlop, updateSwipesResults = true) + ?: return + updateTransition(SwipeTransition(fromScene, result), force = true) } private fun updateSwipes(fromScene: Scene, startedPosition: Offset?, pointersDown: Int) { @@ -188,8 +225,8 @@ internal class SceneGestureHandler( computeFromSceneConsideringAcceleratedSwipe(swipeTransition) val isNewFromScene = fromScene.key != swipeTransition.fromScene - val (targetScene, distance) = - findTargetSceneAndDistance( + val result = + findUserActionResult( fromScene, swipeTransition.dragOffset, updateSwipesResults = isNewFromScene, @@ -200,9 +237,9 @@ internal class SceneGestureHandler( } swipeTransition.dragOffset += acceleratedOffset - if (isNewFromScene || targetScene.key != swipeTransition.toScene) { + if (isNewFromScene || result.toScene != swipeTransition.toScene) { updateTransition( - SwipeTransition(fromScene, targetScene, distance).apply { + SwipeTransition(fromScene, result).apply { this.dragOffset = swipeTransition.dragOffset } ) @@ -211,7 +248,7 @@ internal class SceneGestureHandler( private fun updateSwipesResults(fromScene: Scene) { val (upOrLeftResult, downOrRightResult) = - swipesResults( + computeSwipesResults( fromScene, this.swipes ?: error("updateSwipes() should be called before updateSwipesResults()") ) @@ -220,7 +257,7 @@ internal class SceneGestureHandler( this.downOrRightResult = downOrRightResult } - private fun swipesResults( + private fun computeSwipesResults( fromScene: Scene, swipes: Swipes ): Pair<UserActionResult?, UserActionResult?> { @@ -270,7 +307,7 @@ internal class SceneGestureHandler( } /** - * Returns the target scene and distance from [fromScene] in the direction [directionOffset]. + * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset]. * * @param fromScene the scene from which we look for the target * @param directionOffset signed float that indicates the direction. Positive is down or right @@ -286,66 +323,56 @@ internal class SceneGestureHandler( * [directionOffset] is 0f and both direction are available, it will default to * [upOrLeftResult]. */ - private inline fun findTargetSceneAndDistance( + private fun findUserActionResult( fromScene: Scene, directionOffset: Float, updateSwipesResults: Boolean, - ): Pair<Scene, Float>? { + ): UserActionResult? { if (updateSwipesResults) updateSwipesResults(fromScene) - // Compute the target scene depending on the current offset. return when { upOrLeftResult == null && downOrRightResult == null -> null (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> - upOrLeftResult?.let { result -> - Pair( - layoutImpl.scene(result.toScene), - -fromScene.getAbsoluteDistance(result.distance) - ) - } - else -> - downOrRightResult?.let { result -> - Pair( - layoutImpl.scene(result.toScene), - fromScene.getAbsoluteDistance(result.distance) - ) - } + upOrLeftResult + else -> downOrRightResult } } /** - * A strict version of [findTargetSceneAndDistance] that will return null when there is no Scene - * in [directionOffset] direction + * A strict version of [findUserActionResult] that will return null when there is no Scene in + * [directionOffset] direction */ - private inline fun findTargetSceneAndDistanceStrict( - fromScene: Scene, - directionOffset: Float, - ): Pair<Scene, Float>? { + private fun findUserActionResultStrict(directionOffset: Float): UserActionResult? { return when { - directionOffset > 0f -> - upOrLeftResult?.let { result -> - Pair( - layoutImpl.scene(result.toScene), - -fromScene.getAbsoluteDistance(result.distance), - ) - } - directionOffset < 0f -> - downOrRightResult?.let { result -> - Pair( - layoutImpl.scene(result.toScene), - fromScene.getAbsoluteDistance(result.distance), - ) - } + directionOffset > 0f -> upOrLeftResult + directionOffset < 0f -> downOrRightResult else -> null } } + private fun computeAbsoluteDistance( + fromScene: Scene, + result: UserActionResult, + ): Float { + return if (result == upOrLeftResult) { + -fromScene.getAbsoluteDistance(result.distance) + } else { + check(result == downOrRightResult) + fromScene.getAbsoluteDistance(result.distance) + } + } + internal fun onDragStopped(velocity: Float, canChangeScene: Boolean) { // The state was changed since the drag started; don't do anything. if (!isDrivingTransition) { return } + // Important: Make sure that all the code here references the current transition when + // [onDragStopped] is called, otherwise the callbacks (like onAnimationCompleted()) might + // incorrectly finish a new transition that replaced this one. + val swipeTransition = this.swipeTransition + fun animateTo(targetScene: Scene, targetOffset: Float) { // If the effective current scene changed, it should be reflected right now in the // current scene state, even before the settle animation is ongoing. That way all the @@ -399,8 +426,8 @@ internal class SceneGestureHandler( if (startFromIdlePosition) { // If there is a target scene, we start the overscroll animation. - val (targetScene, distance) = - findTargetSceneAndDistanceStrict(fromScene, velocity) + val result = + findUserActionResultStrict(velocity) ?: run { // We will not animate layoutState.finishTransition(swipeTransition, idleScene = fromScene.key) @@ -408,7 +435,7 @@ internal class SceneGestureHandler( } updateTransition( - SwipeTransition(fromScene, targetScene, distance).apply { + SwipeTransition(fromScene, result).apply { _currentScene = swipeTransition._currentScene } ) @@ -455,6 +482,14 @@ internal class SceneGestureHandler( } } + private fun SwipeTransition(fromScene: Scene, result: UserActionResult): SwipeTransition { + return SwipeTransition( + fromScene, + layoutImpl.scene(result.toScene), + computeAbsoluteDistance(fromScene, result), + ) + } + internal class SwipeTransition( val _fromScene: Scene, val _toScene: Scene, @@ -462,7 +497,7 @@ internal class SceneGestureHandler( * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is * above or to the left of [toScene]. */ - val distance: Float + val distance: Float, ) : TransitionState.Transition(_fromScene.key, _toScene.key) { var _currentScene by mutableStateOf(_fromScene) override val currentScene: SceneKey @@ -495,6 +530,9 @@ internal class SceneGestureHandler( /** Job to check that there is at most one offset animation in progress. */ private var offsetAnimationJob: Job? = null + /** The spec to use when animating this transition to either [fromScene] or [toScene]. */ + lateinit var swipeSpec: SpringSpec<Float> + /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ private fun startOffsetAnimation(job: () -> Job) { cancelOffsetAnimation() @@ -516,13 +554,6 @@ internal class SceneGestureHandler( } } - // TODO(b/290184746): Make this spring spec configurable. - private val animationSpec = - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ) - fun animateOffset( // TODO(b/317063114) The CoroutineScope should be removed. coroutineScope: CoroutineScope, @@ -546,7 +577,7 @@ internal class SceneGestureHandler( offsetAnimatable.animateTo( targetValue = targetOffset, - animationSpec = animationSpec, + animationSpec = swipeSpec, initialVelocity = initialVelocity, ) @@ -651,6 +682,7 @@ internal class SceneNestedScrollHandler( } val source = this + var isIntercepting = false return PriorityNestedScrollConnection( orientation = orientation, @@ -658,7 +690,9 @@ internal class SceneNestedScrollHandler( canChangeScene = offsetBeforeStart == 0f val canInterceptSwipeTransition = - canChangeScene && gestureHandler.isDrivingTransition && offsetAvailable != 0f + canChangeScene && + offsetAvailable != 0f && + gestureHandler.shouldImmediatelyIntercept(startedPosition = null) if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false val swipeTransition = gestureHandler.swipeTransition @@ -676,7 +710,12 @@ internal class SceneNestedScrollHandler( } // Start only if we cannot consume this event - !shouldSnapToIdle + val canStart = !shouldSnapToIdle + if (canStart) { + isIntercepting = true + } + + canStart }, canStartPostScroll = { offsetAvailable, offsetBeforeStart -> val behavior: NestedScrollBehavior = @@ -688,24 +727,31 @@ internal class SceneNestedScrollHandler( val isZeroOffset = offsetBeforeStart == 0f - when (behavior) { - NestedScrollBehavior.DuringTransitionBetweenScenes -> { - canChangeScene = false // unused: added for consistency - false - } - NestedScrollBehavior.EdgeNoPreview -> { - canChangeScene = isZeroOffset - isZeroOffset && hasNextScene(offsetAvailable) - } - NestedScrollBehavior.EdgeWithPreview -> { - canChangeScene = isZeroOffset - hasNextScene(offsetAvailable) - } - NestedScrollBehavior.EdgeAlways -> { - canChangeScene = true - hasNextScene(offsetAvailable) + val canStart = + when (behavior) { + NestedScrollBehavior.DuringTransitionBetweenScenes -> { + canChangeScene = false // unused: added for consistency + false + } + NestedScrollBehavior.EdgeNoPreview -> { + canChangeScene = isZeroOffset + isZeroOffset && hasNextScene(offsetAvailable) + } + NestedScrollBehavior.EdgeWithPreview -> { + canChangeScene = isZeroOffset + hasNextScene(offsetAvailable) + } + NestedScrollBehavior.EdgeAlways -> { + canChangeScene = true + hasNextScene(offsetAvailable) + } } + + if (canStart) { + isIntercepting = false } + + canStart }, canStartPostFling = { velocityAvailable -> val behavior: NestedScrollBehavior = @@ -717,7 +763,13 @@ internal class SceneNestedScrollHandler( // We could start an overscroll animation canChangeScene = false - behavior.canStartOnPostFling && hasNextScene(velocityAvailable) + + val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable) + if (canStart) { + isIntercepting = false + } + + canStart }, canContinueScroll = { true }, canScrollOnFling = false, @@ -726,7 +778,7 @@ internal class SceneNestedScrollHandler( gestureHandler.onDragStarted( pointersDown = 1, startedPosition = null, - overSlop = offsetAvailable, + overSlop = if (isIntercepting) 0f else offsetAvailable, ) }, onScroll = { offsetAvailable -> @@ -761,4 +813,6 @@ internal class SceneNestedScrollHandler( * The number of pixels below which there won't be a visible difference in the transition and from * which the animation can stop. */ -private const val OffsetVisibilityThreshold = 0.5f +// TODO(b/290184746): Have a better default visibility threshold which takes the swipe distance into +// account instead. +internal const val OffsetVisibilityThreshold = 0.5f diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt index 3a55567d69bb..ac423b70fd50 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt @@ -17,7 +17,10 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.SpringSpec import androidx.compose.animation.core.snap +import androidx.compose.animation.core.spring import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.util.fastForEach @@ -36,6 +39,7 @@ import com.android.compose.animation.scene.transformation.Translate /** The transitions configuration of a [SceneTransitionLayout]. */ class SceneTransitions internal constructor( + internal val defaultSwipeSpec: SpringSpec<Float>, internal val transitionSpecs: List<TransitionSpecImpl>, ) { private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpecImpl>>() @@ -91,7 +95,13 @@ internal constructor( TransitionSpecImpl(from, to, TransformationSpec.EmptyProvider) companion object { - val Empty = SceneTransitions(transitionSpecs = emptyList()) + internal val DefaultSwipeSpec = + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold, + ) + + val Empty = SceneTransitions(DefaultSwipeSpec, transitionSpecs = emptyList()) } } @@ -125,15 +135,30 @@ interface TransitionSpec { } interface TransformationSpec { - /** The [AnimationSpec] used to animate the associated transition progress. */ + /** + * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when + * the transition is triggered (i.e. it is not gesture-based). + */ val progressSpec: AnimationSpec<Float> + /** + * The [SpringSpec] used to animate the associated transition progress when the transition was + * started by a swipe and is now animating back to a scene because the user lifted their finger. + * + * If `null`, then the [SceneTransitions.defaultSwipeSpec] will be used. + */ + val swipeSpec: SpringSpec<Float>? + /** The list of [Transformation] applied to elements during this transition. */ val transformations: List<Transformation> companion object { internal val Empty = - TransformationSpecImpl(progressSpec = snap(), transformations = emptyList()) + TransformationSpecImpl( + progressSpec = snap(), + swipeSpec = null, + transformations = emptyList(), + ) internal val EmptyProvider = { Empty } } } @@ -151,6 +176,7 @@ internal class TransitionSpecImpl( val reverse = transformationSpec.invoke() TransformationSpecImpl( progressSpec = reverse.progressSpec, + swipeSpec = reverse.swipeSpec, transformations = reverse.transformations.map { it.reversed() } ) } @@ -166,6 +192,7 @@ internal class TransitionSpecImpl( */ internal class TransformationSpecImpl( override val progressSpec: AnimationSpec<Float>, + override val swipeSpec: SpringSpec<Float>?, override val transformations: List<Transformation>, ) : TransformationSpec { private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index b9c4ac0cd006..61f497818c89 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -19,6 +19,7 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerEvent import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.node.DelegatingNode @@ -94,13 +95,10 @@ private class SwipeToSceneNode( return userActions.keys.any { it is Swipe && it.direction.orientation == orientation } } - private fun startDragImmediately(): Boolean { - // Immediately start the drag if this our transition is currently animating to a scene - // (i.e. the user released their input pointer after swiping in this orientation) and the - // user can't swipe in the other direction. - return gestureHandler.isDrivingTransition && - gestureHandler.swipeTransition.isAnimatingOffset && - !canOppositeSwipe() + private fun startDragImmediately(startedPosition: Offset): Boolean { + // Immediately start the drag if the user can't swipe in the other direction and the gesture + // handler can intercept it. + return !canOppositeSwipe() && gestureHandler.shouldImmediatelyIntercept(startedPosition) } private fun canOppositeSwipe(): Boolean { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index a764a52723af..04e093721824 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -17,6 +17,7 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.SpringSpec import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -31,6 +32,12 @@ fun transitions(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions { @TransitionDsl interface SceneTransitionsBuilder { /** + * The default [AnimationSpec] used when after the user lifts their finger after starting a + * swipe to transition, to animate back into one of the 2 scenes we are transitioning to. + */ + var defaultSwipeSpec: SpringSpec<Float> + + /** * Define the default animation to be played when transitioning [to] the specified scene, from * any scene. For the animation specification to apply only when transitioning between two * specific scenes, use [from] instead. @@ -64,12 +71,20 @@ interface SceneTransitionsBuilder { @TransitionDsl interface TransitionBuilder : PropertyTransformationBuilder { /** - * The [AnimationSpec] used to animate the progress of this transition from `0` to `1` when - * performing programmatic (not input pointer tracking) animations. + * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when + * the transition is triggered (i.e. it is not gesture-based). */ var spec: AnimationSpec<Float> /** + * The [SpringSpec] used to animate the associated transition progress when the transition was + * started by a swipe and is now animating back to a scene because the user lifted their finger. + * + * If `null`, then the [SceneTransitionsBuilder.defaultSwipeSpec] will be used. + */ + var swipeSpec: SpringSpec<Float>? + + /** * Define a progress-based range for the transformations inside [builder]. * * For instance, the following will fade `Foo` during the first half of the transition then it diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt index b96f9bebb08b..df186a15181e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt @@ -19,6 +19,7 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.AnimationSpec import androidx.compose.animation.core.DurationBasedAnimationSpec import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.SpringSpec import androidx.compose.animation.core.VectorConverter import androidx.compose.animation.core.spring import androidx.compose.ui.geometry.Offset @@ -40,10 +41,12 @@ internal fun transitionsImpl( builder: SceneTransitionsBuilder.() -> Unit, ): SceneTransitions { val impl = SceneTransitionsBuilderImpl().apply(builder) - return SceneTransitions(impl.transitionSpecs) + return SceneTransitions(impl.defaultSwipeSpec, impl.transitionSpecs) } private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { + override var defaultSwipeSpec: SpringSpec<Float> = SceneTransitions.DefaultSwipeSpec + val transitionSpecs = mutableListOf<TransitionSpecImpl>() override fun to(to: SceneKey, builder: TransitionBuilder.() -> Unit): TransitionSpec { @@ -67,6 +70,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { val impl = TransitionBuilderImpl().apply(builder) return TransformationSpecImpl( progressSpec = impl.spec, + swipeSpec = impl.swipeSpec, transformations = impl.transformations, ) } @@ -80,6 +84,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder { internal class TransitionBuilderImpl : TransitionBuilder { val transformations = mutableListOf<Transformation>() override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow) + override var swipeSpec: SpringSpec<Float>? = null private var range: TransformationRange? = null private var reversed = false diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index 88363ad24d9a..e8cc0eca0d8b 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -21,7 +21,6 @@ import androidx.compose.material3.Text import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity @@ -118,9 +117,6 @@ class SceneGestureHandlerTest { fun up(fractionOfScreen: Float) = if (fractionOfScreen < 0f) error("use down()") else -down(fractionOfScreen) - // Float tolerance for comparisons - val tolerance = 0.00001f - // Offset y: 10% of the screen val offsetY10 = Offset(x = 0f, y = down(0.1f)) @@ -150,31 +146,42 @@ class SceneGestureHandlerTest { fromScene: SceneKey? = null, toScene: SceneKey? = null, progress: Float? = null, + isUserInputOngoing: Boolean? = null ) { + val transition = transitionState assertWithMessage("transitionState must be Transition") - .that(transitionState is Transition) + .that(transition is Transition) .isTrue() + transition as Transition + if (currentScene != null) assertWithMessage("currentScene does not match") - .that(transitionState.currentScene) + .that(transition.currentScene) .isEqualTo(currentScene) + if (fromScene != null) assertWithMessage("fromScene does not match") - .that((transitionState as? Transition)?.fromScene) + .that(transition.fromScene) .isEqualTo(fromScene) + if (toScene != null) assertWithMessage("toScene does not match") - .that((transitionState as? Transition)?.toScene) + .that(transition.toScene) .isEqualTo(toScene) + if (progress != null) assertWithMessage("progress does not match") - .that((transitionState as? Transition)?.progress) - .isWithin(tolerance) + .that(transition.progress) + .isWithin(0f) // returns true when comparing 0.0f with -0.0f .of(progress) + + if (isUserInputOngoing != null) + assertWithMessage("isUserInputOngoing does not match") + .that(transition.isUserInputOngoing) + .isEqualTo(isUserInputOngoing) } } - @OptIn(ExperimentalTestApi::class) private fun runGestureTest(block: suspend TestGestureScope.() -> Unit) { runMonotonicClockTest { TestGestureScope(coroutineScope = this).block() } } @@ -248,7 +255,7 @@ class SceneGestureHandlerTest { @Test fun onDragReversedDirection_changeToScene() = runGestureTest { // Drag A -> B with progress 0.6 - draggable.onDragStarted(overSlop = up(0.6f)) + draggable.onDragStarted(overSlop = -60f) assertTransition( currentScene = SceneA, fromScene = SceneA, @@ -257,7 +264,7 @@ class SceneGestureHandlerTest { ) // Reverse direction such that A -> C now with 0.4 - draggable.onDelta(down(1f)) + draggable.onDelta(100f) assertTransition( currentScene = SceneA, fromScene = SceneA, @@ -287,7 +294,7 @@ class SceneGestureHandlerTest { navigateToSceneC() // We are on SceneC which has no action in Down direction - draggable.onDragStarted(down(0.1f)) + draggable.onDragStarted(10f) assertTransition( currentScene = SceneC, fromScene = SceneC, @@ -296,7 +303,7 @@ class SceneGestureHandlerTest { ) // Reverse drag direction, it will consume the previous drag - draggable.onDelta(up(0.1f)) + draggable.onDelta(-10f) assertTransition( currentScene = SceneC, fromScene = SceneC, @@ -305,7 +312,7 @@ class SceneGestureHandlerTest { ) // Continue reverse drag direction, it should record progress to Scene B - draggable.onDelta(up(0.1f)) + draggable.onDelta(-10f) assertTransition( currentScene = SceneC, fromScene = SceneC, @@ -557,53 +564,53 @@ class SceneGestureHandlerTest { ) { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) // start scene transition - nestedScroll.scroll(available = Offset(0f, SCREEN_SIZE * firstScroll)) + nestedScroll.scroll(available = Offset(0f, firstScroll)) // stop scene transition (start the "stop animation") nestedScroll.onPreFling(available = Velocity.Zero) // a pre scroll event, that could be intercepted by SceneGestureHandler - nestedScroll.onPreScroll(Offset(0f, SCREEN_SIZE * secondScroll), NestedScrollSource.Drag) + nestedScroll.onPreScroll(Offset(0f, secondScroll), NestedScrollSource.Drag) } @Test fun scrollAndFling_scrollLessThanInterceptable_goToIdleOnCurrentScene() = runGestureTest { - val first = transitionInterceptionThreshold - tolerance - val second = 0.01f + val firstScroll = (transitionInterceptionThreshold - 0.0001f) * SCREEN_SIZE + val secondScroll = 1f - preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) + preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll) assertIdle(SceneA) } @Test fun scrollAndFling_scrollMinInterceptable_interceptPreScrollEvents() = runGestureTest { - val first = transitionInterceptionThreshold + tolerance - val second = 0.01f + val firstScroll = (transitionInterceptionThreshold + 0.0001f) * SCREEN_SIZE + val secondScroll = 1f - preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) + preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll) - assertTransition(progress = first + second) + assertTransition(progress = (firstScroll + secondScroll) / SCREEN_SIZE) } @Test fun scrollAndFling_scrollMaxInterceptable_interceptPreScrollEvents() = runGestureTest { - val first = 1f - transitionInterceptionThreshold - tolerance - val second = 0.01f + val firstScroll = -(1f - transitionInterceptionThreshold - 0.0001f) * SCREEN_SIZE + val secondScroll = -1f - preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) + preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll) - assertTransition(progress = first + second) + assertTransition(progress = -(firstScroll + secondScroll) / SCREEN_SIZE) } @Test fun scrollAndFling_scrollMoreThanInterceptable_goToIdleOnNextScene() = runGestureTest { - val first = 1f - transitionInterceptionThreshold + tolerance - val second = 0.01f + val firstScroll = -(1f - transitionInterceptionThreshold + 0.0001f) * SCREEN_SIZE + val secondScroll = -0.01f - preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) + preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll) - assertIdle(SceneC) + assertIdle(SceneB) } @Test @@ -745,29 +752,75 @@ class SceneGestureHandlerTest { @Test fun startNestedScrollWhileDragging() = runGestureTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) - draggable.onDragStarted(overSlop = down(0.1f)) + + // Start a drag and then stop it, given that + draggable.onDragStarted(overSlop = up(0.1f)) + assertTransition(currentScene = SceneA) assertThat(progress).isEqualTo(0.1f) // now we can intercept the scroll events - nestedScroll.scroll(available = offsetY10) + nestedScroll.scroll(available = -offsetY10) assertThat(progress).isEqualTo(0.2f) // this should be ignored, we are scrolling now! - draggable.onDragStopped(velocityThreshold) + draggable.onDragStopped(-velocityThreshold) assertTransition(currentScene = SceneA) - nestedScroll.scroll(available = offsetY10) + nestedScroll.scroll(available = -offsetY10) assertThat(progress).isEqualTo(0.3f) - nestedScroll.scroll(available = offsetY10) + nestedScroll.scroll(available = -offsetY10) assertThat(progress).isEqualTo(0.4f) - nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold)) - assertTransition(currentScene = SceneC) + nestedScroll.onPreFling(available = Velocity(0f, -velocityThreshold)) + assertTransition(currentScene = SceneB) // wait for the stop animation advanceUntilIdle() - assertIdle(currentScene = SceneC) + assertIdle(currentScene = SceneB) + } + + @Test + fun interceptTransition() = runGestureTest { + // Start at scene C. + navigateToSceneC() + + // Swipe up from the middle to transition to scene B. + val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) + draggable.onDragStarted(startedPosition = middle, overSlop = up(0.1f)) + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + toScene = SceneB, + isUserInputOngoing = true, + ) + + val firstTransition = transitionState + + // During the current gesture, start a new gesture, still in the middle of the screen. We + // should intercept it. Because it is intercepted, the overSlop passed to onDragStarted() + // should be 0f. + assertThat(sceneGestureHandler.shouldImmediatelyIntercept(middle)).isTrue() + draggable.onDragStarted(startedPosition = middle, overSlop = 0f) + + // We should have intercepted the transition, so the transition should be the same object. + assertTransition(currentScene = SceneC, fromScene = SceneC, toScene = SceneB) + assertThat(transitionState).isSameInstanceAs(firstTransition) + + // Start a new gesture from the bottom of the screen. Because swiping up from the bottom of + // C leads to scene A (and not B), the previous transitions is *not* intercepted and we + // instead animate from C to A. + val bottom = Offset(SCREEN_SIZE / 2, SCREEN_SIZE) + assertThat(sceneGestureHandler.shouldImmediatelyIntercept(bottom)).isFalse() + draggable.onDragStarted(startedPosition = bottom, overSlop = up(0.1f)) + + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + toScene = SceneA, + isUserInputOngoing = true, + ) + assertThat(transitionState).isNotSameInstanceAs(firstTransition) } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index 48825fb88096..9a25d43cb62d 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -91,6 +91,16 @@ class SceneTransitionLayoutStateTest { } @Test + fun setTargetScene_transitionToOriginalScene() = runMonotonicClockTest { + val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) + assertThat(state.setTargetScene(TestScenes.SceneB, coroutineScope = this)).isNotNull() + + // Progress is 0f, so we don't animate at all and directly snap back to A. + assertThat(state.setTargetScene(TestScenes.SceneA, coroutineScope = this)).isNull() + assertThat(state.transitionState).isEqualTo(TransitionState.Idle(TestScenes.SceneA)) + } + + @Test fun setTargetScene_coroutineScopeCancelled() = runMonotonicClockTest { val state = MutableSceneTransitionLayoutState(TestScenes.SceneA) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 940335853221..af1a5b8f37b0 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -21,6 +21,9 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.LocalViewConfiguration @@ -63,7 +66,10 @@ class SwipeToSceneTest { /** The content under test. */ @Composable - private fun TestContent(layoutState: SceneTransitionLayoutState) { + private fun TestContent( + layoutState: SceneTransitionLayoutState, + swipesEnabled: () -> Boolean = { true }, + ) { SceneTransitionLayout( state = layoutState, modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName), @@ -71,28 +77,35 @@ class SwipeToSceneTest { scene( TestScenes.SceneA, userActions = - mapOf( - Swipe.Left to TestScenes.SceneB, - Swipe.Down to TestScenes.SceneC, - ), + if (swipesEnabled()) + mapOf( + Swipe.Left to TestScenes.SceneB, + Swipe.Down to TestScenes.SceneC, + Swipe.Up to TestScenes.SceneB, + ) + else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( TestScenes.SceneB, - userActions = mapOf(Swipe.Right to TestScenes.SceneA), + userActions = + if (swipesEnabled()) mapOf(Swipe.Right to TestScenes.SceneA) else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( TestScenes.SceneC, userActions = - mapOf( - Swipe.Down to TestScenes.SceneA, - Swipe(SwipeDirection.Down, pointerCount = 2) to TestScenes.SceneB, - Swipe(SwipeDirection.Right, fromSource = Edge.Left) to TestScenes.SceneB, - Swipe(SwipeDirection.Down, fromSource = Edge.Top) to TestScenes.SceneB, - ), + if (swipesEnabled()) + mapOf( + Swipe.Down to TestScenes.SceneA, + Swipe(SwipeDirection.Down, pointerCount = 2) to TestScenes.SceneB, + Swipe(SwipeDirection.Right, fromSource = Edge.Left) to + TestScenes.SceneB, + Swipe(SwipeDirection.Down, fromSource = Edge.Top) to TestScenes.SceneB, + ) + else emptyMap(), ) { Box(Modifier.fillMaxSize()) } @@ -357,7 +370,7 @@ class SwipeToSceneTest { // detected as a drag event. var touchSlop = 0f - val layoutState = MutableSceneTransitionLayoutState(TestScenes.SceneA) + val layoutState = layoutState() val verticalSwipeDistance = 50.dp assertThat(verticalSwipeDistance).isNotEqualTo(LayoutHeight) @@ -392,4 +405,91 @@ class SwipeToSceneTest { assertThat(transition).isNotNull() assertThat(transition!!.progress).isEqualTo(0.5f) } + + @Test + fun swipeByTouchSlop() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + TestContent(layoutState) + } + + // Swipe down by exactly touchSlop, so that the drag overSlop is 0f. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop), delayMillis = 1_000) + } + + // We should still correctly compute that we are swiping down to scene C. + var transition = layoutState.currentTransition + assertThat(transition).isNotNull() + assertThat(transition?.toScene).isEqualTo(TestScenes.SceneC) + + // Release the finger, animating back to scene A. + rule.onRoot().performTouchInput { up() } + rule.waitForIdle() + assertThat(layoutState.currentTransition).isNull() + assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneA) + + // Swipe up by exactly touchSlop, so that the drag overSlop is 0f. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, -touchSlop), delayMillis = 1_000) + } + + // We should still correctly compute that we are swiping up to scene B. + transition = layoutState.currentTransition + assertThat(transition).isNotNull() + assertThat(transition?.toScene).isEqualTo(TestScenes.SceneB) + + // Release the finger, animating back to scene A. + rule.onRoot().performTouchInput { up() } + rule.waitForIdle() + assertThat(layoutState.currentTransition).isNull() + assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneA) + + // Swipe left by exactly touchSlop, so that the drag overSlop is 0f. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(-touchSlop, 0f), delayMillis = 1_000) + } + + // We should still correctly compute that we are swiping down to scene B. + transition = layoutState.currentTransition + assertThat(transition).isNotNull() + assertThat(transition?.toScene).isEqualTo(TestScenes.SceneB) + } + + @Test + fun swipeEnabledLater() { + val layoutState = MutableSceneTransitionLayoutState(TestScenes.SceneA) + var swipesEnabled by mutableStateOf(false) + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + TestContent(layoutState, swipesEnabled = { swipesEnabled }) + } + + // Drag down from the middle. This should not do anything, because swipes are disabled. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop), delayMillis = 1_000) + } + assertThat(layoutState.currentTransition).isNull() + + // Release finger. + rule.onRoot().performTouchInput { up() } + + // Enable swipes. + swipesEnabled = true + rule.waitForIdle() + + // Drag down from the middle. Now it should start a transition. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop), delayMillis = 1_000) + } + assertThat(layoutState.currentTransition).isNotNull() + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt index ef729921f4cd..140bacaec738 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt @@ -18,6 +18,7 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.SpringSpec import androidx.compose.animation.core.TweenSpec +import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.transformation.Transformation @@ -188,6 +189,40 @@ class TransitionDslTest { ) } + @Test + fun springSpec() { + val defaultSpec = spring<Float>(stiffness = 1f) + val specFromAToC = spring<Float>(stiffness = 2f) + val transitions = transitions { + defaultSwipeSpec = defaultSpec + + from(TestScenes.SceneA, to = TestScenes.SceneB) { + // Default swipe spec. + } + from(TestScenes.SceneA, to = TestScenes.SceneC) { swipeSpec = specFromAToC } + } + + assertThat(transitions.defaultSwipeSpec).isSameInstanceAs(defaultSpec) + + // A => B does not have a custom spec. + assertThat( + transitions + .transitionSpec(from = TestScenes.SceneA, to = TestScenes.SceneB) + .transformationSpec() + .swipeSpec + ) + .isNull() + + // A => C has a custom swipe spec. + assertThat( + transitions + .transitionSpec(from = TestScenes.SceneA, to = TestScenes.SceneC) + .transformationSpec() + .swipeSpec + ) + .isSameInstanceAs(specFromAToC) + } + companion object { private val TRANSFORMATION_RANGE = Correspondence.transforming<Transformation, TransformationRange?>( diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt index e0506046ee54..34c4dfb4bc54 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withContext @@ -518,6 +519,7 @@ class CustomizationProviderClientImpl( awaitClose { context.contentResolver.unregisterContentObserver(observer) } } .onStart { emit(Unit) } + .flowOn(backgroundDispatcher) } private fun String.toIntent( 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 c6d612d5dc79..c193d14220de 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 @@ -208,45 +208,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { } @Test - fun onImeVisibilityChanged_false_doesNothing() = + fun onImeDismissed() = testScope.runTest { val events by collectValues(bouncerInteractor.onImeHiddenByUser) assertThat(events).isEmpty() - underTest.onImeVisibilityChanged(isVisible = false) - assertThat(events).isEmpty() - } - - @Test - fun onImeVisibilityChanged_falseAfterTrue_emitsOnImeHiddenByUserEvent() = - testScope.runTest { - val events by collectValues(bouncerInteractor.onImeHiddenByUser) - assertThat(events).isEmpty() - - underTest.onImeVisibilityChanged(isVisible = true) - assertThat(events).isEmpty() - - underTest.onImeVisibilityChanged(isVisible = false) - assertThat(events).hasSize(1) - - underTest.onImeVisibilityChanged(isVisible = true) + underTest.onImeDismissed() assertThat(events).hasSize(1) - - underTest.onImeVisibilityChanged(isVisible = false) - assertThat(events).hasSize(2) - } - - @Test - fun onImeVisibilityChanged_falseAfterTrue_whileLockedOut_doesNothing() = - testScope.runTest { - val events by collectValues(bouncerInteractor.onImeHiddenByUser) - assertThat(events).isEmpty() - underTest.onImeVisibilityChanged(isVisible = true) - setLockout(true) - - underTest.onImeVisibilityChanged(isVisible = false) - - assertThat(events).isEmpty() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt new file mode 100644 index 000000000000..a8fe16b12e1b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dock.DockManager +import com.android.systemui.dock.dockManager +import com.android.systemui.dock.fakeDockManager +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class CommunalSceneStartableTest : SysuiTestCase() { + private val kosmos = testKosmos() + + private lateinit var underTest: CommunalSceneStartable + + @Before + fun setUp() = + with(kosmos) { + underTest = + CommunalSceneStartable( + dockManager = dockManager, + communalInteractor = communalInteractor, + keyguardTransitionInteractor = keyguardTransitionInteractor, + applicationScope = applicationCoroutineScope, + bgScope = applicationCoroutineScope, + ) + .apply { start() } + } + + @Test + fun keyguardGoesAway_forceBlankScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalInteractor.desiredScene) + + communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.GONE, + testScope = this + ) + + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + } + } + + @Test + fun deviceDreaming_forceBlankScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalInteractor.desiredScene) + + communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.DREAMING, + testScope = this + ) + + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + } + } + + @Test + fun deviceDocked_forceCommunalScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalInteractor.desiredScene) + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + + updateDocked(true) + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + testScope = this + ) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.DREAMING, + testScope = this + ) + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + } + } + + @Test + fun deviceDocked_doesNotForceCommunalIfTransitioningFromCommunal() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalInteractor.desiredScene) + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + + updateDocked(true) + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.LOCKSCREEN, + testScope = this + ) + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + } + } + + @Test + fun deviceAsleep_forceBlankSceneAfterTimeout() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalInteractor.desiredScene) + communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.OFF, + testScope = this + ) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + + advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY) + + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + } + } + + @Test + fun deviceAsleep_wakesUpBeforeTimeout_noChangeInScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalInteractor.desiredScene) + communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.OFF, + testScope = this + ) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY / 2) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.OFF, + to = KeyguardState.GLANCEABLE_HUB, + testScope = this + ) + + advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + } + } + + @Test + fun dockingOnLockscreen_forcesCommunal() = + with(kosmos) { + testScope.runTest { + communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + val scene by collectLastValue(communalInteractor.desiredScene) + + // device is docked while on the lockscreen + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.LOCKSCREEN, + testScope = this + ) + updateDocked(true) + + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY) + assertThat(scene).isEqualTo(CommunalSceneKey.Communal) + } + } + + @Test + fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() = + with(kosmos) { + testScope.runTest { + communalInteractor.onSceneChanged(CommunalSceneKey.Blank) + val scene by collectLastValue(communalInteractor.desiredScene) + + // device is docked while on the lockscreen + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.LOCKSCREEN, + testScope = this + ) + updateDocked(true) + + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY / 2) + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + + // dream starts shortly after docking + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DREAMING, + testScope = this + ) + advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY) + assertThat(scene).isEqualTo(CommunalSceneKey.Blank) + } + } + + private fun TestScope.updateDocked(docked: Boolean) = + with(kosmos) { + runCurrent() + fakeDockManager.setIsDocked(docked) + fakeDockManager.setDockEvent(DockManager.STATE_DOCKED) + runCurrent() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index a083e7cf22c7..ee01bf9c26e4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -125,7 +125,7 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(mainUser) keyguardRepository.setKeyguardShowing(true) - runCurrent() + communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isTrue() } @@ -138,7 +138,8 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(true) userRepository.setSelectedUserInfo(mainUser) - runCurrent() + keyguardRepository.setKeyguardShowing(true) + communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isFalse() } @@ -152,7 +153,7 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(secondaryUser) keyguardRepository.setKeyguardShowing(true) - runCurrent() + communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isFalse() } @@ -166,12 +167,26 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setIsEncryptedOrLockdown(false) userRepository.setSelectedUserInfo(mainUser) keyguardRepository.setDreaming(true) - runCurrent() + communalRepository.setCommunalEnabledState(true) assertThat(isAvailable).isTrue() } @Test + fun isCommunalAvailable_communalDisabled_false() = + testScope.runTest { + val isAvailable by collectLastValue(underTest.isCommunalAvailable) + assertThat(isAvailable).isFalse() + + keyguardRepository.setIsEncryptedOrLockdown(false) + userRepository.setSelectedUserInfo(mainUser) + keyguardRepository.setKeyguardShowing(true) + communalRepository.setCommunalEnabledState(false) + + assertThat(isAvailable).isFalse() + } + + @Test fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() = testScope.runTest { // Keyguard showing, and tutorial completed. @@ -633,6 +648,14 @@ class CommunalInteractorTest : SysuiTestCase() { verify(editWidgetsActivityStarter).startActivity() } + @Test + fun showWidgetEditor_withPreselectedKey_startsActivity() = + testScope.runTest { + val widgetKey = CommunalContentModel.KEY.widget(123) + underTest.showWidgetEditor(preselectedKey = widgetKey) + verify(editWidgetsActivityStarter).startActivity(widgetKey) + } + private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget { val timer = mock(SmartspaceTarget::class.java) whenever(timer.smartspaceTargetId).thenReturn(id) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt index 352463f5a198..6c87e0f2eb23 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt @@ -34,20 +34,15 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository -import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class CommunalTutorialInteractorTest : SysuiTestCase() { - @Mock lateinit var user: UserInfo - private val kosmos = testKosmos() private val testScope = kosmos.testScope @@ -60,14 +55,14 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { @Before fun setUp() { - MockitoAnnotations.initMocks(this) - keyguardRepository = kosmos.fakeKeyguardRepository communalTutorialRepository = kosmos.fakeCommunalTutorialRepository communalRepository = kosmos.fakeCommunalRepository communalInteractor = kosmos.communalInteractor userRepository = kosmos.fakeUserRepository + userRepository.setUserInfos(listOf(MAIN_USER_INFO)) + underTest = kosmos.communalTutorialInteractor } @@ -204,12 +199,17 @@ class CommunalTutorialInteractorTest : SysuiTestCase() { private suspend fun setCommunalAvailable(available: Boolean) { if (available) { communalRepository.setIsCommunalEnabled(true) + communalRepository.setCommunalEnabledState(true) keyguardRepository.setIsEncryptedOrLockdown(false) - whenever(user.isMain).thenReturn(true) - userRepository.setUserInfos(listOf(user)) - userRepository.setSelectedUserInfo(user) + userRepository.setSelectedUserInfo(MAIN_USER_INFO) + keyguardRepository.setKeyguardShowing(true) } else { - keyguardRepository.setIsEncryptedOrLockdown(true) + communalRepository.setIsCommunalEnabled(false) + communalRepository.setCommunalEnabledState(false) } } + + private companion object { + val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index a2dec5ff8830..273d1cd55626 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -129,6 +129,19 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } @Test + fun selectedKey_onReorderWidgets_isCleared() = + testScope.runTest { + val selectedKey by collectLastValue(underTest.selectedKey) + + val key = CommunalContentModel.KEY.widget(123) + underTest.setSelectedKey(key) + assertThat(selectedKey).isEqualTo(key) + + underTest.onReorderWidgetStart() + assertThat(selectedKey).isNull() + } + + @Test fun reorderWidget_uiEventLogging_start() { underTest.onReorderWidgetStart() verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index c814f3f1db6a..0723e8306f71 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository +import com.android.systemui.communal.data.repository.fakeCommunalRepository import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository import com.android.systemui.communal.domain.interactor.communalInteractor @@ -90,6 +91,8 @@ class CommunalViewModelTest : SysuiTestCase() { mediaRepository = kosmos.fakeCommunalMediaRepository userRepository = kosmos.fakeUserRepository + kosmos.fakeCommunalRepository.setCommunalEnabledState(true) + underTest = CommunalViewModel( testScope, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt index 112b0c797854..a3654b6e8963 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt @@ -20,6 +20,7 @@ import android.content.pm.UserInfo import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.fakeCommunalRepository import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.applicationCoroutineScope @@ -121,9 +122,10 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { private suspend fun setCommunalAvailable(available: Boolean) = with(kosmos) { - fakeKeyguardRepository.setIsEncryptedOrLockdown(!available) + fakeKeyguardRepository.setIsEncryptedOrLockdown(false) fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO) fakeKeyguardRepository.setKeyguardShowing(true) + fakeCommunalRepository.setCommunalEnabledState(available) } private companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/binder/LiftToRunFaceAuthBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/binder/LiftToRunFaceAuthBinderTest.kt new file mode 100644 index 000000000000..e9e85c9f210b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/binder/LiftToRunFaceAuthBinderTest.kt @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.domain.ui.binder + +import android.content.packageManager +import android.content.pm.PackageManager +import android.hardware.Sensor +import android.hardware.TriggerEventListener +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository +import com.android.systemui.deviceentry.ui.binder.liftToRunFaceAuthBinder +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.data.repository.fakePowerRepository +import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.sensors.asyncSensorManager +import kotlinx.coroutines.ExperimentalCoroutinesApi +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.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@ExperimentalCoroutinesApi +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class LiftToRunFaceAuthBinderTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val sensorManager = kosmos.asyncSensorManager + private val powerRepository = kosmos.fakePowerRepository + private val keyguardRepository = kosmos.fakeKeyguardRepository + private val bouncerRepository = kosmos.keyguardBouncerRepository + private val biometricSettingsRepository = kosmos.biometricSettingsRepository + private val packageManager = kosmos.packageManager + + @Captor private lateinit var triggerEventListenerCaptor: ArgumentCaptor<TriggerEventListener> + @Mock private lateinit var mockSensor: Sensor + + private val underTest = kosmos.liftToRunFaceAuthBinder + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true) + whenever(sensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)).thenReturn(mockSensor) + } + + @Test + fun doNotListenForGesture() = + testScope.runTest { + start() + verifyNeverRequestsTriggerSensor() + } + + @Test + fun awakeKeyguard_listenForGesture() = + testScope.runTest { + start() + givenAwakeKeyguard(true) + runCurrent() + verifyRequestTriggerSensor() + } + + @Test + fun faceNotEnrolled_listenForGesture() = + testScope.runTest { + start() + givenAwakeKeyguard(true) + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) + runCurrent() + verifyNeverRequestsTriggerSensor() + } + + @Test + fun notInteractive_doNotListenForGesture() = + testScope.runTest { + start() + givenAwakeKeyguard(true) + powerRepository.setInteractive(false) + runCurrent() + verifyNeverRequestsTriggerSensor() + } + + @Test + fun primaryBouncer_listenForGesture() = + testScope.runTest { + start() + givenAwakeKeyguard(false) + givenPrimaryBouncerShowing() + runCurrent() + verifyRequestTriggerSensor() + } + + @Test + fun alternateBouncer_listenForGesture() = + testScope.runTest { + start() + givenAwakeKeyguard(false) + givenAlternateBouncerShowing() + runCurrent() + verifyRequestTriggerSensor() + } + + @Test + fun restartListeningForGestureAfterSensorTrigger() = + testScope.runTest { + start() + givenAwakeKeyguard(true) + runCurrent() + verifyRequestTriggerSensor() + clearInvocations(sensorManager) + + triggerEventListenerCaptor.value.onTrigger(null) + runCurrent() + verifyRequestTriggerSensor() + } + + @Test + fun cancelTriggerSensor_keyguardNotAwakeAnymore() = + testScope.runTest { + start() + givenAwakeKeyguard(true) + runCurrent() + verifyRequestTriggerSensor() + + givenAwakeKeyguard(false) + runCurrent() + verifyCancelTriggerSensor() + } + + private fun start() { + underTest.start() + biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) + givenAwakeKeyguard(false) + givenBouncerNotShowing() + } + + private fun givenAwakeKeyguard(isAwake: Boolean) { + powerRepository.setInteractive(isAwake) + keyguardRepository.setKeyguardShowing(isAwake) + keyguardRepository.setKeyguardOccluded(false) + } + + private fun givenPrimaryBouncerShowing() { + bouncerRepository.setPrimaryShow(true) + bouncerRepository.setAlternateVisible(false) + } + + private fun givenBouncerNotShowing() { + bouncerRepository.setPrimaryShow(false) + bouncerRepository.setAlternateVisible(false) + } + + private fun givenAlternateBouncerShowing() { + bouncerRepository.setPrimaryShow(false) + bouncerRepository.setAlternateVisible(true) + } + + private fun verifyRequestTriggerSensor() { + verify(sensorManager).requestTriggerSensor(capture(triggerEventListenerCaptor), any()) + } + + private fun verifyNeverRequestsTriggerSensor() { + verify(sensorManager, never()).requestTriggerSensor(any(), any()) + } + + private fun verifyCancelTriggerSensor() { + verify(sensorManager).cancelTriggerSensor(any(), any()) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFakeKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFakeKosmos.kt new file mode 100644 index 000000000000..06275fa226a5 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFakeKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dock + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.dockManager: DockManager by Kosmos.Fixture { fakeDockManager } +val Kosmos.fakeDockManager: DockManagerFake by Kosmos.Fixture { DockManagerFake() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 78ae8b119c69..c01c79dd2601 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -574,7 +574,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { @Test fun isEncryptedOrLockdown() = - testScope.runTest { + TestScope(mainDispatcher).runTest { whenever(userTracker.userId).thenReturn(0) whenever(keyguardUpdateMonitor.isEncryptedOrLockdown(0)).thenReturn(true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 34f703bc0ca7..db414b724b63 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.dock.DockManager import com.android.systemui.dock.DockManagerFake import com.android.systemui.flags.FakeFeatureFlags @@ -49,16 +50,17 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.testKosmos import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -82,6 +84,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var devicePolicyManager: DevicePolicyManager + @Mock private lateinit var shadeInteractor: ShadeInteractor @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger private lateinit var underTest: KeyguardQuickAffordanceInteractor @@ -95,8 +98,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { private lateinit var dockManager: DockManagerFake private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository - private val kosmos = testKosmos() - @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -183,7 +184,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = withDeps.keyguardInteractor, - shadeInteractor = kosmos.shadeInteractor, + shadeInteractor = shadeInteractor, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, @@ -198,6 +199,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { backgroundDispatcher = testDispatcher, appContext = context, ) + + whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f)) } @Test @@ -344,6 +347,25 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test + fun quickAffordance_updateOncePerShadeExpansion() = + testScope.runTest { + val shadeExpansion = MutableStateFlow(0f) + whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion) + + val collectedValue by + collectValues( + underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START) + ) + + val initialSize = collectedValue.size + for (i in 0..10) { + shadeExpansion.value = i / 10f + } + + assertThat(collectedValue.size).isEqualTo(initialSize + 1) + } + + @Test fun quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManager() = testScope.runTest { whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index 30ac34402ffd..6fc5be1e376d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -19,7 +19,7 @@ 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.domain.interactor.primaryBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -52,11 +52,9 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { val testScope = kosmos.testScope val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository - val primaryBouncerInteractor = kosmos.primaryBouncerInteractor + val primaryBouncerInteractor = kosmos.mockPrimaryBouncerInteractor val sysuiStatusBarStateController = kosmos.sysuiStatusBarStateController - val underTest by lazy { - kosmos.primaryBouncerToGoneTransitionViewModel - } + val underTest by lazy { kosmos.primaryBouncerToGoneTransitionViewModel } @Before fun setUp() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt index d277fcab3690..1545e7444ffb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.data.repository +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class AutoAddSettingsRepositoryTest : SysuiTestCase() { private val secureSettings = FakeSettings() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt index 3db676d68f42..ee7a97a0846a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.pipeline.data.repository import android.content.ComponentName import android.content.SharedPreferences +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -29,6 +30,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt index f7c3b213730c..3418977c3211 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.data.repository +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -39,6 +40,7 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class TileSpecSettingsRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt index 9516c2181ac0..9e99fc006acc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.qs.pipeline.data.repository +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -9,6 +10,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class TilesSettingConverterTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt index 36e860e37ffa..1ca3c0637824 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.qs.pipeline.data.repository +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -24,6 +25,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class UserAutoAddRepositoryTest : SysuiTestCase() { private val secureSettings = FakeSettings() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt index d4a9fabd6806..58fc10917d44 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.qs.pipeline.data.repository +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -23,6 +24,7 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations @SmallTest +@EnabledOnRavenwood @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) class UserTileSpecRepositoryTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt index 30d1822b28da..57bb77f46e40 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.pipeline.data.restoreprocessors import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -31,6 +32,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class WorkTileRestoreProcessorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt index 4454a3cb15fc..f185ed57de74 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import android.content.ComponentName +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R @@ -28,6 +29,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class AutoAddableSettingListTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt index d153e9d1d361..cfb84a7a6709 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -35,6 +36,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class AutoAddableSettingTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt index ec139e4c515e..ea8d87358172 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -35,6 +36,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class CallbackControllerAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt index 4fae532d4174..9bb591e1ae7c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -41,6 +42,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class CastAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt index 9e2d1f885e2d..b925b27bfcc3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -40,6 +41,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class DataSaverAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt index 0116bd9575d8..188c6a949104 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -43,6 +44,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class DeviceControlsAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt index e7ea9a66450c..02699ddad089 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -40,6 +41,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class HotspotAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt index 19ac63c36cab..6d6fd754341d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -43,6 +44,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class ReduceBrightColorsAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt index d645ee34619b..633e494b3195 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import android.content.ComponentName import android.content.pm.PackageManager +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.res.R @@ -51,6 +52,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class SafetyCenterAutoAddableTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt index 83ff35d8022d..c5c76eb77152 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.autoaddable +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -37,6 +38,7 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class WalletAutoAddableTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt index 54b03a90229b..2ea12ef4c88f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.pipeline.domain.interactor +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -46,6 +47,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class AutoAddInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt index 0d9711588a1f..d38c19b5bc4a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.qs.pipeline.domain.interactor +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -28,6 +29,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) @SmallTest +@EnabledOnRavenwood class PanelInteractorImplTest : SysuiTestCase() { @Mock private lateinit var shadeController: ShadeController diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt index b2a9783d2e60..0b3144ab3084 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.qs.pipeline.domain.interactor +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -26,6 +27,7 @@ import org.mockito.Mockito.inOrder @RunWith(AndroidJUnit4::class) @SmallTest +@EnabledOnRavenwood class RestoreReconciliationInteractorTest : SysuiTestCase() { private val tileSpecRepository = FakeTileSpecRepository() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt index 558e7694b54c..869ab6c24fce 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.pipeline.shared import android.content.ComponentName +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -25,6 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class TileSpecTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt index c1049773cabf..bf48784407b8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt @@ -24,6 +24,7 @@ import android.content.pm.PackageManager import android.content.pm.PackageManager.ResolveInfoFlags import android.content.pm.ResolveInfo import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -44,6 +45,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class QSTileIntentUserInputHandlerTest : SysuiTestCase() { @Mock private lateinit var packageManager: PackageManager diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt index 9861606fd1b1..fd09e3ca4bb8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.base.analytics +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId @@ -33,6 +34,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class QSTileAnalyticsTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt index 2bdc154dd885..a1f885c64312 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt @@ -20,6 +20,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.RestrictedLockUtils @@ -45,6 +46,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class DisabledByPolicyInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt index 937744db500e..89b9b7f30297 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.airplate.domain.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -36,6 +37,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class AirplaneModeTileDataInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt index 81bde8188f5e..8982d810ad8a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.impl.airplate.domain.interactor +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import android.telephony.TelephonyManager import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -37,6 +38,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class AirplaneModeTileUserActionInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt index 8c612acad887..abaf808f3f91 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -35,6 +36,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class ColorCorrectionTileDataInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt index 3049cc079a1c..3bc53b273e89 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -32,6 +33,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class ColorCorrectionTileUserActionInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt index 7f7490d9ecc3..a9e39354d131 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import android.testing.LeakCheck import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -37,6 +38,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class FlashlightTileDataInteractorTest : SysuiTestCase() { private lateinit var controller: FakeFlashlightController diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt index 24c7bfb2d324..75b07eedab95 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.inversion.domain.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -35,6 +36,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class ColorInversionTileDataInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt index 99bae18d43d4..f574f793fbf2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.inversion.domain.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -32,6 +33,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class ColorInversionUserActionInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt index 8fdc93be4ba2..9adf57ac458a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.location.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import android.testing.LeakCheck import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -38,6 +39,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class LocationTileDataInteractorTest : SysuiTestCase() { private lateinit var controller: FakeLocationController diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt index 0fb8ae697190..8b21cb4a97d5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.impl.location.interactor +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -42,6 +43,7 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class LocationTileUserActionInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt index 4b9625107745..f24723a2a9f3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.saver.domain import android.content.SharedPreferences +import android.platform.test.annotations.EnabledOnRavenwood import android.testing.LeakCheck import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -37,6 +38,7 @@ import org.mockito.Mockito.verify /** Test [DataSaverDialogDelegate]. */ @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class DataSaverDialogDelegateTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt index 819bd03437f4..daee22d0c45c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.saver.domain.interactor import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import android.testing.LeakCheck import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -36,6 +37,7 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class DataSaverTileDataInteractorTest : SysuiTestCase() { private val controller: FakeDataSaverController = FakeDataSaverController(LeakCheck()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt index 004ec6250e7e..eea6d161d4ff 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.uimodenight.domain import android.app.UiModeManager +import android.platform.test.annotations.EnabledOnRavenwood import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -39,6 +40,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class UiModeNightTileUserActionInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt index 5eca8caa7d15..40971a87480d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.viewmodel +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -27,6 +28,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class QSTileConfigProviderTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt index 3a0ebdbd6a17..a8bc8d6b36b6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.viewmodel import android.os.UserHandle +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.android.systemui.SysuiTestCase @@ -50,6 +51,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @MediumTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class QSTileViewModelTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt index 22fb152aee44..18cdd71e25b6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.viewmodel +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.android.settingslib.RestrictedLockUtils @@ -53,6 +54,7 @@ import org.mockito.MockitoAnnotations /** Tests all possible [QSTileUserAction]s. If you need */ @MediumTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) class QSTileViewModelUserInputTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt index 18a7320bf29d..d1bc686385a1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.ui.adapter +import android.platform.test.annotations.EnabledOnRavenwood import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -25,6 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest +@EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class QSSceneAdapterTest : SysuiTestCase() { @Test 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 1e5ebd0c2acd..504ded30a06c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.model.SysUiState +import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.powerInteractor @@ -244,7 +245,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { kosmos.fakeDeviceEntryRepository.setUnlocked(false) val displayTracker = FakeDisplayTracker(context) - val sysUiState = SysUiState(displayTracker) + val sysUiState = SysUiState(displayTracker, kosmos.sceneContainerPlugin) val startable = SceneContainerStartable( applicationScope = testScope.backgroundScope, @@ -775,14 +776,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { } /** Emulates the dismissal of the IME (soft keyboard). */ - private suspend fun TestScope.dismissIme( - showImeBeforeDismissing: Boolean = true, - ) { + private fun TestScope.dismissIme() { (bouncerViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let { - if (showImeBeforeDismissing) { - it.onImeVisibilityChanged(true) - } - it.onImeVisibilityChanged(false) + it.onImeDismissed() runCurrent() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index bf99a8687aa4..942fbc229e8a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -174,105 +174,6 @@ class SceneInteractorTest : SysuiTestCase() { } @Test - fun transitioning_idle_false() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Shade) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isFalse() - } - - @Test - fun transitioning_wrongFromScene_false() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Lockscreen, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isFalse() - } - - @Test - fun transitioning_wrongToScene_false() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.QuickSettings, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - underTest.setTransitionState(transitionState) - - assertThat(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen).value).isFalse() - } - - @Test - fun transitioning_correctFromAndToScenes_true() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isTrue() - } - - @Test - fun transitioning_updates() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Shade) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isFalse() - - transitionState.value = - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - assertThat(transitioning).isTrue() - - transitionState.value = ObservableTransitionState.Idle(SceneKey.Lockscreen) - assertThat(transitioning).isFalse() - } - - @Test fun isTransitionUserInputOngoing_idle_false() = testScope.runTest { val transitionState = diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt index 6bbe900c8779..1ef07facf8d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImplTest.kt @@ -16,71 +16,37 @@ package com.android.systemui.shade.domain.interactor +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.SysUITestComponent -import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.TestMocksModule -import com.android.systemui.collectLastValue -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FakeFeatureFlagsClassicModule -import com.android.systemui.flags.Flags -import com.android.systemui.runCurrent -import com.android.systemui.runTest -import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.user.domain.UserDomainLayerModule -import com.android.systemui.util.mockito.mock +import com.android.systemui.testKosmos import com.google.common.truth.Truth -import dagger.BindsInstance -import dagger.Component +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@RunWith(AndroidJUnit4::class) class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() { + val kosmos = testKosmos() + val testScope = kosmos.testScope + val sceneInteractor = kosmos.sceneInteractor - @SysUISingleton - @Component( - modules = - [ - SysUITestModule::class, - UserDomainLayerModule::class, - ] - ) - interface TestComponent : SysUITestComponent<ShadeAnimationInteractorSceneContainerImpl> { - val sceneInteractor: SceneInteractor - - @Component.Factory - interface Factory { - fun create( - @BindsInstance test: SysuiTestCase, - featureFlags: FakeFeatureFlagsClassicModule, - mocks: TestMocksModule, - ): TestComponent - } - } - - private val dozeParameters: DozeParameters = mock() - - private val testComponent: TestComponent = - DaggerShadeAnimationInteractorSceneContainerImplTest_TestComponent.factory() - .create( - test = this, - featureFlags = - FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, - mocks = - TestMocksModule( - dozeParameters = dozeParameters, - ), - ) + val underTest = kosmos.shadeAnimationInteractorSceneContainerImpl @Test fun isAnyCloseAnimationRunning_qsToShade() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.isAnyCloseAnimationRunning) // WHEN transitioning from QS to Shade @@ -103,10 +69,10 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun isAnyCloseAnimationRunning_qsToGone_userInputNotOngoing() = - testComponent.runTest() { + testScope.runTest() { val actual by collectLastValue(underTest.isAnyCloseAnimationRunning) - // WHEN transitioning from QS to Gone with no ongoing user input + // WHEN transitioning from QS to Gone lwith no ongoing user input val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( @@ -126,7 +92,7 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun isAnyCloseAnimationRunning_qsToGone_userInputOngoing() = - testComponent.runTest() { + testScope.runTest() { val actual by collectLastValue(underTest.isAnyCloseAnimationRunning) // WHEN transitioning from QS to Gone with user input ongoing @@ -149,7 +115,7 @@ class ShadeAnimationInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun updateIsLaunchingActivity() = - testComponent.runTest { + testScope.runTest { Truth.assertThat(underTest.isLaunchingActivity.value).isEqualTo(false) underTest.setIsLaunchingActivity(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt index 71a7420636cc..4e82feb3a2c6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt @@ -19,124 +19,65 @@ package com.android.systemui.shade.domain.interactor import android.app.StatusBarManager.DISABLE2_NONE import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS -import android.content.pm.UserInfo -import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.SysUITestComponent -import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.TestMocksModule -import com.android.systemui.collectLastValue -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FakeFeatureFlagsClassicModule -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +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.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.DozeTransitionModel 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.power.data.repository.FakePowerRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.res.R -import com.android.systemui.runCurrent -import com.android.systemui.runTest -import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.shade.data.repository.FakeShadeRepository +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel -import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository -import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository -import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository +import com.android.systemui.statusbar.phone.dozeParameters +import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository +import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository +import com.android.systemui.testKosmos import com.android.systemui.user.data.model.UserSwitcherSettingsModel -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.UserDomainLayerModule -import com.android.systemui.util.mockito.mock +import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import dagger.BindsInstance -import dagger.Component -import kotlinx.coroutines.runBlocking -import org.junit.Before +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@RunWith(AndroidJUnit4::class) class ShadeInteractorImplTest : SysuiTestCase() { - - @SysUISingleton - @Component( - modules = - [ - SysUITestModule::class, - UserDomainLayerModule::class, - ] - ) - interface TestComponent : SysUITestComponent<ShadeInteractorImpl> { - - val configurationRepository: FakeConfigurationRepository - val deviceProvisioningRepository: FakeDeviceProvisioningRepository - val disableFlagsRepository: FakeDisableFlagsRepository - val keyguardRepository: FakeKeyguardRepository - val keyguardTransitionRepository: FakeKeyguardTransitionRepository - val powerRepository: FakePowerRepository - val sceneInteractor: SceneInteractor - val shadeRepository: FakeShadeRepository - val userRepository: FakeUserRepository - val userSetupRepository: FakeUserSetupRepository - - @Component.Factory - interface Factory { - fun create( - @BindsInstance test: SysuiTestCase, - featureFlags: FakeFeatureFlagsClassicModule, - mocks: TestMocksModule, - ): TestComponent - } - } - - private val dozeParameters: DozeParameters = mock() - - private val testComponent: TestComponent = - DaggerShadeInteractorImplTest_TestComponent.factory() - .create( - test = this, - featureFlags = - FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, - mocks = - TestMocksModule( - dozeParameters = dozeParameters, - ), - ) - - @Before - fun setUp() { - runBlocking { - val userInfos = - listOf( - UserInfo( - /* id= */ 0, - /* name= */ "zero", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_PRIMARY or - UserInfo.FLAG_ADMIN or - UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - ) - testComponent.apply { - userRepository.setUserInfos(userInfos) - userRepository.setSelectedUserInfo(userInfos[0]) - } - } - } + val kosmos = testKosmos() + val testScope = kosmos.testScope + val configurationRepository = kosmos.fakeConfigurationRepository + val deviceProvisioningRepository = kosmos.fakeDeviceProvisioningRepository + val disableFlagsRepository = kosmos.fakeDisableFlagsRepository + val keyguardRepository = kosmos.fakeKeyguardRepository + val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository + val powerRepository = kosmos.fakePowerRepository + val sceneInteractor = kosmos.sceneInteractor + val shadeRepository = kosmos.fakeShadeRepository + val userRepository = kosmos.fakeUserRepository + val userSetupRepository = kosmos.fakeUserSetupRepository + val dozeParameters = kosmos.dozeParameters + + val underTest = kosmos.shadeInteractorImpl @Test fun isShadeEnabled_matchesDisableFlagsRepo() = - testComponent.runTest { + testScope.runTest { val actual by collectLastValue(underTest.isShadeEnabled) disableFlagsRepository.disableFlags.value = @@ -150,7 +91,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_deviceNotProvisioned_false() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(false) val actual by collectLastValue(underTest.isExpandToQsEnabled) @@ -160,7 +101,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_userNotSetupAndSimpleUserSwitcher_false() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetUp(false) @@ -173,7 +114,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_shadeNotEnabled_false() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetUp(true) @@ -189,7 +130,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_quickSettingsNotEnabled_false() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetUp(true) @@ -204,7 +145,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_dozing_false() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) userSetupRepository.setUserSetUp(true) disableFlagsRepository.disableFlags.value = @@ -221,7 +162,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_userSetup_true() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = @@ -238,7 +179,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_notSimpleUserSwitcher_true() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = @@ -255,7 +196,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToDozingUpdates() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = @@ -283,7 +224,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToDisableUpdates() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = @@ -315,7 +256,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isExpandToQsEnabled_respondsToUserUpdates() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setDeviceProvisioned(true) keyguardRepository.setIsDozing(false) disableFlagsRepository.disableFlags.value = @@ -344,7 +285,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun fullShadeExpansionWhenShadeLocked() = - testComponent.runTest { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) @@ -355,7 +296,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun fullShadeExpansionWhenStatusBarStateIsNotShadeLocked() = - testComponent.runTest { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) @@ -369,7 +310,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun shadeExpansionWhenInSplitShadeAndQsExpanded() = - testComponent.runTest { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is enabled and QS is expanded @@ -386,7 +327,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() = - testComponent.runTest { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is not enabled and QS is expanded @@ -402,7 +343,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() = - testComponent.runTest { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is not enabled and QS is expanded @@ -416,7 +357,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun anyExpansion_shadeGreater() = - testComponent.runTest() { + testScope.runTest() { // WHEN shade is more expanded than QS shadeRepository.setLegacyShadeExpansion(.5f) shadeRepository.setQsExpansion(0f) @@ -428,7 +369,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun anyExpansion_qsGreater() = - testComponent.runTest() { + testScope.runTest() { // WHEN qs is more expanded than shade shadeRepository.setLegacyShadeExpansion(0f) shadeRepository.setQsExpansion(.5f) @@ -440,7 +381,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadeDraggedUpAndDown() = - testComponent.runTest() { + testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) @@ -496,7 +437,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadeExpanded() = - testComponent.runTest() { + testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) @@ -531,7 +472,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadePartiallyExpanded() = - testComponent.runTest() { + testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) @@ -572,7 +513,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadeCollapsed() = - testComponent.runTest() { + testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade expanded and not tracking input shadeRepository.setLegacyShadeExpansion(1f) @@ -607,7 +548,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun userInteractingWithQs_qsDraggedUpAndDown() = - testComponent.runTest() { + testScope.runTest() { val actual by collectLastValue(underTest.isUserInteractingWithQs) // GIVEN qs collapsed and not tracking input shadeRepository.setQsExpansion(0f) @@ -663,7 +604,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isShadeTouchable_isFalse_whenFrpIsActive() = - testComponent.runTest { + testScope.runTest { deviceProvisioningRepository.setFactoryResetProtectionActive(true) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -677,7 +618,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isShadeTouchable_isFalse_whenDeviceAsleepAndNotPulsing() = - testComponent.runTest { + testScope.runTest { powerRepository.updateWakefulness( rawState = WakefulnessState.ASLEEP, lastWakeReason = WakeSleepReason.POWER_BUTTON, @@ -704,7 +645,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isShadeTouchable_isTrue_whenDeviceAsleepAndPulsing() = - testComponent.runTest { + testScope.runTest { powerRepository.updateWakefulness( rawState = WakefulnessState.ASLEEP, lastWakeReason = WakeSleepReason.POWER_BUTTON, @@ -731,7 +672,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isShadeTouchable_isFalse_whenStartingToSleepAndNotControlScreenOff() = - testComponent.runTest { + testScope.runTest { powerRepository.updateWakefulness( rawState = WakefulnessState.STARTING_TO_SLEEP, lastWakeReason = WakeSleepReason.POWER_BUTTON, @@ -753,7 +694,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isShadeTouchable_isTrue_whenStartingToSleepAndControlScreenOff() = - testComponent.runTest { + testScope.runTest { powerRepository.updateWakefulness( rawState = WakefulnessState.STARTING_TO_SLEEP, lastWakeReason = WakeSleepReason.POWER_BUTTON, @@ -775,7 +716,7 @@ class ShadeInteractorImplTest : SysuiTestCase() { @Test fun isShadeTouchable_isTrue_whenNotAsleep() = - testComponent.runTest { + testScope.runTest { powerRepository.updateWakefulness( rawState = WakefulnessState.AWAKE, lastWakeReason = WakeSleepReason.POWER_BUTTON, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt index 6e6e4384b7b3..682c4ef0b8e7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt @@ -16,108 +16,45 @@ package com.android.systemui.shade.domain.interactor -import android.content.pm.UserInfo -import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.SysUITestComponent -import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.TestMocksModule -import com.android.systemui.collectLastValue -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FakeFeatureFlagsClassicModule -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +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.shared.model.StatusBarState -import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.kosmos.testScope import com.android.systemui.res.R -import com.android.systemui.runCurrent -import com.android.systemui.runTest -import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.UserDomainLayerModule -import com.android.systemui.util.mockito.mock +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.shade.data.repository.fakeShadeRepository +import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat -import dagger.BindsInstance -import dagger.Component -import kotlinx.coroutines.runBlocking -import org.junit.Before +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@RunWith(AndroidJUnit4::class) class ShadeInteractorLegacyImplTest : SysuiTestCase() { + val kosmos = testKosmos() + val testScope = kosmos.testScope + val configurationRepository = kosmos.fakeConfigurationRepository + val keyguardRepository = kosmos.fakeKeyguardRepository + val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository + val sceneInteractor = kosmos.sceneInteractor + val shadeRepository = kosmos.fakeShadeRepository + val userRepository = kosmos.fakeUserRepository - @SysUISingleton - @Component( - modules = - [ - SysUITestModule::class, - UserDomainLayerModule::class, - ] - ) - interface TestComponent : SysUITestComponent<ShadeInteractorLegacyImpl> { - - val configurationRepository: FakeConfigurationRepository - val keyguardRepository: FakeKeyguardRepository - val keyguardTransitionRepository: FakeKeyguardTransitionRepository - val powerRepository: FakePowerRepository - val sceneInteractor: SceneInteractor - val shadeRepository: FakeShadeRepository - val userRepository: FakeUserRepository - - @Component.Factory - interface Factory { - fun create( - @BindsInstance test: SysuiTestCase, - featureFlags: FakeFeatureFlagsClassicModule, - mocks: TestMocksModule, - ): TestComponent - } - } - - private val dozeParameters: DozeParameters = mock() - - private val testComponent: TestComponent = - DaggerShadeInteractorLegacyImplTest_TestComponent.factory() - .create( - test = this, - featureFlags = - FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, - mocks = - TestMocksModule( - dozeParameters = dozeParameters, - ), - ) - - @Before - fun setUp() { - runBlocking { - val userInfos = - listOf( - UserInfo( - /* id= */ 0, - /* name= */ "zero", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_PRIMARY or - UserInfo.FLAG_ADMIN or - UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - ) - testComponent.apply { - userRepository.setUserInfos(userInfos) - userRepository.setSelectedUserInfo(userInfos[0]) - } - } - } + val underTest = kosmos.shadeInteractorLegacyImpl @Test fun fullShadeExpansionWhenShadeLocked() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) @@ -128,7 +65,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun fullShadeExpansionWhenStatusBarStateIsNotShadeLocked() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) @@ -142,7 +79,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun shadeExpansionWhenInSplitShadeAndQsExpanded() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is enabled and QS is expanded @@ -159,7 +96,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun shadeExpansionWhenNotInSplitShadeAndQsExpanded() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is not enabled and QS is expanded @@ -175,7 +112,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun shadeExpansionWhenNotInSplitShadeAndQsCollapsed() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.shadeExpansion) // WHEN split shade is not enabled and QS is expanded @@ -189,7 +126,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadeDraggedUpAndDown() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) @@ -245,7 +182,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadeExpanded() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) @@ -280,7 +217,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadePartiallyExpanded() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade collapsed and not tracking input shadeRepository.setLegacyShadeExpansion(0f) @@ -321,7 +258,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun userInteractingWithShade_shadeCollapsed() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.isUserInteractingWithShade) // GIVEN shade expanded and not tracking input shadeRepository.setLegacyShadeExpansion(1f) @@ -356,7 +293,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { @Test fun userInteractingWithQs_qsDraggedUpAndDown() = - testComponent.runTest() { + testScope.runTest { val actual by collectLastValue(underTest.isUserInteractingWithQs) // GIVEN qs collapsed and not tracking input shadeRepository.setQsExpansion(0f) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt index 310b86f908c5..bf136cdd817e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt @@ -16,114 +16,48 @@ package com.android.systemui.shade.domain.interactor -import android.content.pm.UserInfo -import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.SysUITestComponent -import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.TestMocksModule -import com.android.systemui.collectLastValue -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FakeFeatureFlagsClassicModule -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository +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.keyguardTransitionRepository import com.android.systemui.keyguard.shared.model.StatusBarState -import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.kosmos.testScope import com.android.systemui.res.R -import com.android.systemui.runCurrent -import com.android.systemui.runTest -import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.UserDomainLayerModule -import com.android.systemui.util.mockito.mock +import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.userRepository import com.google.common.truth.Truth -import dagger.BindsInstance -import dagger.Component +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking -import org.junit.Before -import org.junit.Ignore +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Test +import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest +@RunWith(AndroidJUnit4::class) class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { - @SysUISingleton - @Component( - modules = - [ - SysUITestModule::class, - UserDomainLayerModule::class, - ] - ) - interface TestComponent : SysUITestComponent<ShadeInteractorSceneContainerImpl> { - - val configurationRepository: FakeConfigurationRepository - val keyguardRepository: FakeKeyguardRepository - val keyguardTransitionRepository: FakeKeyguardTransitionRepository - val powerRepository: FakePowerRepository - val sceneInteractor: SceneInteractor - val shadeRepository: FakeShadeRepository - val userRepository: FakeUserRepository - - @Component.Factory - interface Factory { - fun create( - @BindsInstance test: SysuiTestCase, - featureFlags: FakeFeatureFlagsClassicModule, - mocks: TestMocksModule, - ): TestComponent - } - } - - private val dozeParameters: DozeParameters = mock() - - private val testComponent: TestComponent = - DaggerShadeInteractorSceneContainerImplTest_TestComponent.factory() - .create( - test = this, - featureFlags = - FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }, - mocks = - TestMocksModule( - dozeParameters = dozeParameters, - ), - ) - - @Before - fun setUp() { - runBlocking { - val userInfos = - listOf( - UserInfo( - /* id= */ 0, - /* name= */ "zero", - /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_PRIMARY or - UserInfo.FLAG_ADMIN or - UserInfo.FLAG_FULL, - UserManager.USER_TYPE_FULL_SYSTEM, - ), - ) - testComponent.apply { - userRepository.setUserInfos(userInfos) - userRepository.setSelectedUserInfo(userInfos[0]) - } - } - } + val kosmos = testKosmos() + val testComponent = kosmos.testScope + val configurationRepository = kosmos.fakeConfigurationRepository + val keyguardRepository = kosmos.fakeKeyguardRepository + val keyguardTransitionRepository = kosmos.keyguardTransitionRepository + val sceneInteractor = kosmos.sceneInteractor + val userRepository = kosmos.userRepository + + val underTest = kosmos.shadeInteractorSceneContainerImpl - @Ignore("b/309825977") @Test fun qsExpansionWhenInSplitShadeAndQsExpanded() = - testComponent.runTest() { + testComponent.runTest { val actual by collectLastValue(underTest.qsExpansion) // WHEN split shade is enabled and QS is expanded @@ -148,10 +82,9 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { Truth.assertThat(actual).isEqualTo(.3f) } - @Ignore("b/309825977") @Test fun qsExpansionWhenNotInSplitShadeAndQsExpanded() = - testComponent.runTest() { + testComponent.runTest { val actual by collectLastValue(underTest.qsExpansion) // WHEN split shade is not enabled and QS is expanded @@ -179,7 +112,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun qsFullscreen_falseWhenTransitioning() = - testComponent.runTest() { + testComponent.runTest { val actual by collectLastValue(underTest.isQsFullscreen) // WHEN scene transition active @@ -203,7 +136,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun qsFullscreen_falseWhenIdleNotQS() = - testComponent.runTest() { + testComponent.runTest { val actual by collectLastValue(underTest.isQsFullscreen) // WHEN Idle but not on QuickSettings scene @@ -221,7 +154,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun qsFullscreen_trueWhenIdleQS() = - testComponent.runTest() { + testComponent.runTest { val actual by collectLastValue(underTest.isQsFullscreen) // WHEN Idle on QuickSettings scene @@ -239,7 +172,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun lockscreenShadeExpansion_idle_onScene() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene val key = SceneKey.Shade val expansion = underTest.sceneBasedExpansion(sceneInteractor, key) @@ -256,7 +189,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun lockscreenShadeExpansion_idle_onDifferentScene() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.Shade) val expansionAmount by collectLastValue(expansion) @@ -274,7 +207,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun lockscreenShadeExpansion_transitioning_toScene() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene val key = SceneKey.QuickSettings val expansion = underTest.sceneBasedExpansion(sceneInteractor, key) @@ -312,7 +245,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun lockscreenShadeExpansion_transitioning_fromScene() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene val key = SceneKey.QuickSettings val expansion = underTest.sceneBasedExpansion(sceneInteractor, key) @@ -349,7 +282,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { } fun isQsBypassingShade_goneToQs() = - testComponent.runTest() { + testComponent.runTest { val actual by collectLastValue(underTest.isQsBypassingShade) // WHEN transitioning from QS directly to Gone @@ -372,7 +305,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { } fun isQsBypassingShade_shadeToQs() = - testComponent.runTest() { + testComponent.runTest { val actual by collectLastValue(underTest.isQsBypassingShade) // WHEN transitioning from QS to Shade @@ -396,7 +329,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an expansion flow based on transitions to and from a scene val expansion = underTest.sceneBasedExpansion(sceneInteractor, SceneKey.QuickSettings) val expansionAmount by collectLastValue(expansion) @@ -433,7 +366,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun userInteracting_idle() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.Shade val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) @@ -450,7 +383,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun userInteracting_transitioning_toScene_programmatic() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) @@ -488,7 +421,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun userInteracting_transitioning_toScene_userInputDriven() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) @@ -526,7 +459,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun userInteracting_transitioning_fromScene_programmatic() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) @@ -564,7 +497,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun userInteracting_transitioning_fromScene_userInputDriven() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene val key = SceneKey.QuickSettings val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key) @@ -602,7 +535,7 @@ class ShadeInteractorSceneContainerImplTest : SysuiTestCase() { @Test fun userInteracting_transitioning_toAndFromDifferentScenes() = - testComponent.runTest() { + testComponent.runTest { // GIVEN an interacting flow based on transitions to and from a scene val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, SceneKey.Shade) val interacting by collectLastValue(interactingFlow) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index e9a2a3befb03..c0aaab3ad6e1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -9,8 +9,6 @@ import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel @@ -22,8 +20,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -74,74 +70,6 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { } @Test - fun isTransitioning_idle_false() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Shade)) - ) - - assertThat(isTransitioning).isFalse() - } - - @Test - fun isTransitioning_shadeToQs_true() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.QuickSettings, - progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - ) - - assertThat(isTransitioning).isTrue() - } - - @Test - fun isTransitioning_qsToShade_true() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Shade, - progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - ) - - assertThat(isTransitioning).isTrue() - } - - @Test - fun isTransitioning_otherTransition_false() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Shade, - progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - ) - - assertThat(isTransitioning).isFalse() - } - - @Test fun mobileSubIds_update() = testScope.runTest { val mobileSubIds by collectLastValue(underTest.mobileSubIds) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java index 7274c0c65e69..452895745f95 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java @@ -31,6 +31,7 @@ import android.content.res.Configuration; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.ravenwood.RavenwoodRule; import android.testing.TestableLooper.RunWithLooper; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -66,8 +67,11 @@ public class SystemUIDialogTest extends SysuiTestCase { @Mock private SystemUIDialog.Delegate mDelegate; + // TODO(b/292141694): build out Ravenwood support for DeviceFlagsValueProvider + // Ravenwood already has solid support for SetFlagsRule, but CheckFlagsRule will be added soon @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood() ? null + : DeviceFlagsValueProvider.createCheckFlagsRule(); @Before public void setup() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt new file mode 100644 index 000000000000..471c8d851879 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.bottombar.ui.viewmodel + +import android.content.Intent +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.activityStarter +import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.eq +import com.android.systemui.volume.panel.volumePanelViewModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class BottomBarViewModelTest : SysuiTestCase() { + + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + @Captor private lateinit var intentCaptor: ArgumentCaptor<Intent> + + private val kosmos = testKosmos() + + private lateinit var underTest: BottomBarViewModel + + private fun initUnderTest() { + underTest = with(kosmos) { BottomBarViewModel(activityStarter, volumePanelViewModel) } + } + + @Test + fun onDoneClicked_hidesPanel() { + with(kosmos) { + testScope.runTest { + initUnderTest() + underTest.onDoneClicked() + runCurrent() + + val volumePanelState by collectLastValue(volumePanelViewModel.volumePanelState) + assertThat(volumePanelState!!.isVisible).isFalse() + } + } + } + + @Test + fun onSettingsClicked_dismissesPanelAndNavigatesToSettings() { + with(kosmos) { + testScope.runTest { + initUnderTest() + underTest.onSettingsClicked() + + runCurrent() + + val volumePanelState by collectLastValue(volumePanelViewModel.volumePanelState) + assertThat(volumePanelState!!.isVisible).isFalse() + verify(activityStarter).startActivity(capture(intentCaptor), eq(true)) + assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_SOUND_SETTINGS) + } + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt new file mode 100644 index 000000000000..6256eece65dd --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.volume.panel.availableCriteria +import com.android.systemui.volume.panel.criteriaByKey +import com.android.systemui.volume.panel.defaultCriteria +import com.android.systemui.volume.panel.domain.model.ComponentModel +import com.android.systemui.volume.panel.enabledComponents +import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey +import com.android.systemui.volume.panel.unavailableCriteria +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ComponentsInteractorImplTest : SysuiTestCase() { + + private val kosmos = Kosmos() + + private lateinit var underTest: ComponentsInteractor + + private fun initUnderTest() { + underTest = + with(kosmos) { + ComponentsInteractorImpl( + enabledComponents, + defaultCriteria, + testScope.backgroundScope, + criteriaByKey, + ) + } + } + + @Test + fun componentsAvailability_checked() { + with(kosmos) { + testScope.runTest { + enabledComponents = + setOf( + BOTTOM_BAR, + COMPONENT_1, + COMPONENT_2, + ) + criteriaByKey = + mapOf( + BOTTOM_BAR to availableCriteria, + COMPONENT_1 to unavailableCriteria, + COMPONENT_2 to availableCriteria, + ) + initUnderTest() + + val components by collectLastValue(underTest.components) + + assertThat(components) + .containsExactly( + ComponentModel(BOTTOM_BAR, true), + ComponentModel(COMPONENT_1, false), + ComponentModel(COMPONENT_2, true), + ) + } + } + } + + @Test + fun noCriteria_fallbackToDefaultCriteria() { + with(kosmos) { + testScope.runTest { + enabledComponents = + setOf( + BOTTOM_BAR, + COMPONENT_1, + COMPONENT_2, + ) + criteriaByKey = + mapOf( + BOTTOM_BAR to availableCriteria, + COMPONENT_2 to availableCriteria, + ) + defaultCriteria = unavailableCriteria + initUnderTest() + + val components by collectLastValue(underTest.components) + + assertThat(components) + .containsExactly( + ComponentModel(BOTTOM_BAR, true), + ComponentModel(COMPONENT_1, false), + ComponentModel(COMPONENT_2, true), + ) + } + } + } + + private companion object { + const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar" + const val COMPONENT_1: VolumePanelComponentKey = "test_component:1" + const val COMPONENT_2: VolumePanelComponentKey = "test_component:2" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryTest.kt new file mode 100644 index 000000000000..3dbf23e3dc58 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.ui.composable + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.testKosmos +import com.android.systemui.volume.panel.componentByKey +import com.android.systemui.volume.panel.mockVolumePanelUiComponentProvider +import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ComponentsFactoryTest : SysuiTestCase() { + + private val kosmos = testKosmos() + + private lateinit var underTest: ComponentsFactory + + private fun initUnderTest() { + underTest = ComponentsFactory(kosmos.componentByKey) + } + + @Test + fun existingComponent_created() { + kosmos.componentByKey = mapOf(TEST_COMPONENT to kosmos.mockVolumePanelUiComponentProvider) + initUnderTest() + + val component = underTest.createComponent(TEST_COMPONENT) + + Truth.assertThat(component).isNotNull() + } + + @Test(expected = IllegalArgumentException::class) + fun componentAbsence_throws() { + kosmos.componentByKey = emptyMap() + initUnderTest() + + underTest.createComponent(TEST_COMPONENT) + } + + private companion object { + const val TEST_COMPONENT: VolumePanelComponentKey = "test_component" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt index e5fb9426e8e2..35d96989e35d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt @@ -16,7 +16,78 @@ package com.android.systemui.volume.panel.ui.viewmodel -class DefaultComponentsLayoutManagerTest { +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.testKosmos +import com.android.systemui.volume.panel.mockVolumePanelUiComponent +import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey +import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager +import com.android.systemui.volume.panel.ui.layout.DefaultComponentsLayoutManager +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith - // TODO(b/318080198) Write tests +@SmallTest +@RunWith(AndroidJUnit4::class) +class DefaultComponentsLayoutManagerTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val underTest: ComponentsLayoutManager = DefaultComponentsLayoutManager() + + @Test + fun bottomBar_isSet() { + val bottomBarComponentState = + ComponentState(BOTTOM_BAR, kosmos.mockVolumePanelUiComponent, false) + val layout = + underTest.layout( + VolumePanelState(0, false), + setOf( + bottomBarComponentState, + ComponentState(COMPONENT_1, kosmos.mockVolumePanelUiComponent, false), + ComponentState(COMPONENT_2, kosmos.mockVolumePanelUiComponent, false), + ) + ) + + Truth.assertThat(layout.bottomBarComponent).isEqualTo(bottomBarComponentState) + } + + @Test + fun componentsAreInOrder() { + val bottomBarComponentState = + ComponentState(BOTTOM_BAR, kosmos.mockVolumePanelUiComponent, false) + val component1State = ComponentState(COMPONENT_1, kosmos.mockVolumePanelUiComponent, false) + val component2State = ComponentState(COMPONENT_2, kosmos.mockVolumePanelUiComponent, false) + val layout = + underTest.layout( + VolumePanelState(0, false), + setOf( + bottomBarComponentState, + component1State, + component2State, + ) + ) + + Truth.assertThat(layout.contentComponents[0]).isEqualTo(component1State) + Truth.assertThat(layout.contentComponents[1]).isEqualTo(component2State) + } + + @Test(expected = IllegalStateException::class) + fun bottomBarAbsence_throwsException() { + val component1State = ComponentState(COMPONENT_1, kosmos.mockVolumePanelUiComponent, false) + val component2State = ComponentState(COMPONENT_2, kosmos.mockVolumePanelUiComponent, false) + underTest.layout( + VolumePanelState(0, false), + setOf( + component1State, + component2State, + ) + ) + } + + private companion object { + const val BOTTOM_BAR: VolumePanelComponentKey = "bottom_bar" + const val COMPONENT_1: VolumePanelComponentKey = "test_component:1" + const val COMPONENT_2: VolumePanelComponentKey = "test_component:2" + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt index 9795237a3e7e..c4c9cc608afa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt @@ -16,7 +16,120 @@ package com.android.systemui.volume.panel.ui.viewmodel -class VolumePanelViewModelTest { +import android.content.res.Configuration +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.policy.fakeConfigurationController +import com.android.systemui.testKosmos +import com.android.systemui.volume.panel.componentByKey +import com.android.systemui.volume.panel.componentsLayoutManager +import com.android.systemui.volume.panel.criteriaByKey +import com.android.systemui.volume.panel.dagger.factory.KosmosVolumePanelComponentFactory +import com.android.systemui.volume.panel.mockVolumePanelUiComponentProvider +import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey +import com.android.systemui.volume.panel.ui.layout.FakeComponentsLayoutManager +import com.android.systemui.volume.panel.unavailableCriteria +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith - // TODO(b/318080198) Write tests +@SmallTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +class VolumePanelViewModelTest : SysuiTestCase() { + + private val kosmos = + testKosmos().apply { + componentsLayoutManager = FakeComponentsLayoutManager { it.key == BOTTOM_BAR } + } + + private val testableResources = context.orCreateTestableResources + + private lateinit var underTest: VolumePanelViewModel + + private fun initUnderTest() { + underTest = + VolumePanelViewModel( + testableResources.resources, + KosmosVolumePanelComponentFactory(kosmos), + kosmos.fakeConfigurationController, + ) + } + + @Test + fun dismissingPanel_changesVisibility() { + with(kosmos) { + testScope.runTest { + initUnderTest() + assertThat(underTest.volumePanelState.value.isVisible).isTrue() + + underTest.dismissPanel() + runCurrent() + + assertThat(underTest.volumePanelState.value.isVisible).isFalse() + } + } + } + + @Test + fun orientationChanges_panelOrientationChanges() { + with(kosmos) { + testScope.runTest { + initUnderTest() + val volumePanelState by collectLastValue(underTest.volumePanelState) + testableResources.overrideConfiguration( + Configuration().apply { orientation = Configuration.ORIENTATION_PORTRAIT } + ) + assertThat(volumePanelState!!.orientation) + .isEqualTo(Configuration.ORIENTATION_PORTRAIT) + + fakeConfigurationController.onConfigurationChanged( + Configuration().apply { orientation = Configuration.ORIENTATION_LANDSCAPE } + ) + runCurrent() + + assertThat(volumePanelState!!.orientation) + .isEqualTo(Configuration.ORIENTATION_LANDSCAPE) + } + } + } + + @Test + fun components_areReturned() { + with(kosmos) { + testScope.runTest { + componentByKey = + mapOf( + COMPONENT_1 to mockVolumePanelUiComponentProvider, + COMPONENT_2 to mockVolumePanelUiComponentProvider, + BOTTOM_BAR to mockVolumePanelUiComponentProvider, + ) + criteriaByKey = mapOf(COMPONENT_2 to unavailableCriteria) + initUnderTest() + + val componentsLayout by collectLastValue(underTest.componentsLayout) + runCurrent() + + assertThat(componentsLayout!!.contentComponents).hasSize(2) + assertThat(componentsLayout!!.contentComponents[0].key).isEqualTo(COMPONENT_1) + assertThat(componentsLayout!!.contentComponents[0].isVisible).isTrue() + assertThat(componentsLayout!!.contentComponents[1].key).isEqualTo(COMPONENT_2) + assertThat(componentsLayout!!.contentComponents[1].isVisible).isFalse() + assertThat(componentsLayout!!.bottomBarComponent.key).isEqualTo(BOTTOM_BAR) + assertThat(componentsLayout!!.bottomBarComponent.isVisible).isTrue() + } + } + } + + private companion object { + const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar" + const val COMPONENT_1: VolumePanelComponentKey = "test_component:1" + const val COMPONENT_2: VolumePanelComponentKey = "test_component:2" + } } diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml index f801990e314e..3b2eb15956e4 100644 --- a/packages/SystemUI/res-keyguard/values-af/strings.xml +++ b/packages/SystemUI/res-keyguard/values-af/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans vinnig"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans stadig"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laaiproses is onderbreek om battery te beskerm"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Gaan die laaibykomstigheid na"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk is gesluit"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Geen SIM nie"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Onbruikbare SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml index 356eebb074c2..c3d141c36a53 100644 --- a/packages/SystemUI/res-keyguard/values-am/strings.xml +++ b/packages/SystemUI/res-keyguard/values-am/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> - ባትሪን ለመጠበቅ ኃይል መሙላት በይቆይ ላይ"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • የኃይል መሙላት መለዋወጫን ይፈትሹ"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"አውታረ መረብ ተቆልፏል"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ምንም SIM የለም"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ጥቅም ላይ የማይውል ሲም።"</string> diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index 707388e68aea..cf8341dfd44f 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن سريعًا"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن ببطء"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • الشحن معلّق لحماية البطارية"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • يُرجى فحص ملحق الشحن."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"الشبكة مؤمّنة"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"لا تتوفر شريحة SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"شريحة SIM غير قابلة للاستخدام."</string> diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml index 8b9d30666aa2..d11a9c182cf3 100644 --- a/packages/SystemUI/res-keyguard/values-as/strings.xml +++ b/packages/SystemUI/res-keyguard/values-as/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চ্চার্জ কৰি থকা হৈছে"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্ৰুত গতিৰে চাৰ্জ কৰি থকা হৈছে"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • লাহে লাহে চাৰ্জ কৰি থকা হৈছে"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বেটাৰী সুৰক্ষিত কৰিবলৈ চাৰ্জিং স্থগিত ৰখা হৈছে"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চাৰ্জিঙৰ সৈতে জড়িত আনুষংগিক সামগ্ৰী পৰীক্ষা কৰক"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটৱর্ক লক কৰা অৱস্থাত আছে"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"কোনো ছিম নাই"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ব্যৱহাৰ কৰিব নোৱৰা ছিম।"</string> diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml index 39d8032fb203..4de32d91b817 100644 --- a/packages/SystemUI/res-keyguard/values-az/strings.xml +++ b/packages/SystemUI/res-keyguard/values-az/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Enerji yığır"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sürətlə enerji yığır"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş enerji yığır"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareyanı qorumaq üçün şarj gözlədilir"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj aksesuarını yoxlayın"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Şəbəkə kilidlidir"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM yoxdur"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"İstifadəyə yararsız SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml index 519e5f6cabd4..d23ff41e761a 100644 --- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je na čekanju da bi se zaštitila baterija"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Proverite dodatnu opremu za punjenje"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Neupotrebljiv SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml index 15bc616f7efb..e704c3c84583 100644 --- a/packages/SystemUI/res-keyguard/values-be/strings.xml +++ b/packages/SystemUI/res-keyguard/values-be/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе зарадка"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе хуткая зарадка"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе павольная зарадка"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарадка прыпынена, каб абараніць акумулятар"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Праверце зарадную прыладу"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сетка заблакіравана"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Няма SIM-карты"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Непрыдатная для выкарыстання SIM-карта."</string> diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml index 7e2e2b3fa41b..795d4b83c857 100644 --- a/packages/SystemUI/res-keyguard/values-bg/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бързо"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бавно"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зареждането е поставено на пауза с цел запазване на батерията"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проверете аксесоара за зареждане"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заключена"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Няма SIM карта"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Неизползваема SIM карта."</string> diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index 2f7d3ba107cd..e333ddd1ea4a 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ হচ্ছে"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্রুত চার্জ হচ্ছে"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ধীরে চার্জ হচ্ছে"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ব্যাটারিকে সুরক্ষিত রাখতে চার্জিং হোল্ড করা হয়েছে"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জিং অ্যাক্সেসরি চেক করুন"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটওয়ার্ক লক করা আছে"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"কোনও সিম নেই"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ব্যবহারযোগ্য নয় এমন সিম।"</string> diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml index e35298b830ab..75fe286dfe2f 100644 --- a/packages/SystemUI/res-keyguard/values-bs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo punjenje"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo punjenje"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je na čekanju radi zaštite baterije"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Provjera dodatne opreme za punjenje"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Neupotrebljiv SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml index 0fcc49103810..4047d7ca662a 100644 --- a/packages/SystemUI/res-keyguard/values-ca/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant ràpidament"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • La càrrega s\'ha posat en espera per protegir la bateria"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Revisa l\'accessori de càrrega"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"La xarxa està bloquejada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hi ha cap SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"La SIM no es pot utilitzar."</string> diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml index 3764032b9c21..ea9a68367652 100644 --- a/packages/SystemUI/res-keyguard/values-cs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjení"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rychlé nabíjení"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pomalé nabíjení"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjení je odloženo za účelem ochrany baterie"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Zkontrolujte nabíjecí příslušenství"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Síť je blokována"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Žádná SIM karta"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM kartu nelze použít."</string> diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml index 8b04c4636230..8794bc051f27 100644 --- a/packages/SystemUI/res-keyguard/values-da/strings.xml +++ b/packages/SystemUI/res-keyguard/values-da/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader hurtigt"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader langsomt"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladningen er sat på pause for at beskytte batteriet"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tjek opladningstilbehøret"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netværket er låst"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Intet SIM-kort"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Deaktiveret SIM-kort."</string> diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml index f122f4901d3c..9e80b741c87d 100644 --- a/packages/SystemUI/res-keyguard/values-de/strings.xml +++ b/packages/SystemUI/res-keyguard/values-de/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladevorgang zum Schutz des Akkus angehalten"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladezubehör prüfen"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Keine SIM-Karte"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-Karte ist nicht nutzbar."</string> diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml index 9c05a29dcdf6..b7d32501a8c0 100644 --- a/packages/SystemUI/res-keyguard/values-el/strings.xml +++ b/packages/SystemUI/res-keyguard/values-el/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Φόρτιση"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Γρήγορη φόρτιση"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Αργή φόρτιση"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Η φόρτιση τέθηκε σε αναμονή για την προστασία της μπαταρίας"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ελέγξτε το αξεσουάρ φόρτισης"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Κλειδωμένο δίκτυο"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Δεν υπάρχει SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Η SIM δεν μπορεί να χρησιμοποιηθεί."</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml index fc8790c9902f..d0461c1a56bb 100644 --- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging on hold to protect battery"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Check charging accessory"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml index fc8790c9902f..d0461c1a56bb 100644 --- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging on hold to protect battery"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Check charging accessory"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml index fc8790c9902f..d0461c1a56bb 100644 --- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging on hold to protect battery"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Check charging accessory"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml index 3851b07143a2..43aea8da6670 100644 --- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se detuvo la carga para proteger la batería"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Verifica el accesorio de carga"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hay ninguna tarjeta SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Tarjeta SIM inutilizable."</string> diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml index c0fccddef915..9022a3d44474 100644 --- a/packages/SystemUI/res-keyguard/values-es/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carga pausada para proteger la batería"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Comprueba el accesorio de carga"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hay ninguna SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"No se puede usar la SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml index 5f75c87e5420..c0d5e2f23f35 100644 --- a/packages/SystemUI/res-keyguard/values-et/strings.xml +++ b/packages/SystemUI/res-keyguard/values-et/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kiirlaadimine"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aeglane laadimine"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • laadimine on aku kaitsmiseks ootele pandud"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • kontrollige laadimistarvikut"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Võrk on lukus"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM-i pole"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-i ei saa kasutada."</string> diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index 486531ac991d..c9ac7e921e7b 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • kargatze-prozesua zain dago bateria babesteko"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Eman begiratu bat kargatzeko osagarriari"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ez dago SIMik"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ezin da erabili SIMa."</string> diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index 4822487b2029..b1b1f8a5277c 100644 --- a/packages/SystemUI/res-keyguard/values-fa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ شدن"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ سریع"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آهستهآهسته شارژ میشود"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • برای محافظت از باتری، شارژ موقتاً متوقف شده است"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • لوازم شارژ را بررسی کنید"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"شبکه قفل شد"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"سیمکارتی وجود ندارد"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"سیمکارت قابلاستفاده نیست."</string> diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml index 39b0529e7a26..f5a67594ced0 100644 --- a/packages/SystemUI/res-keyguard/values-fi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan nopeasti"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan hitaasti"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lataus on keskeytetty akun suojaamiseksi"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tarkista latauslisävaruste"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Verkko lukittu"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ei SIM-korttia"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-korttia ei voi käyttää."</string> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index fcf505282dd5..bca8dd7909a7 100644 --- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • La recharge a été mise en pause pour protéger la pile"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Vérifier l\'accessoire de recharge"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Aucune carte SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"La carte SIM est inutilisable."</string> diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml index ffa0b1d5f26d..97d2081555f9 100644 --- a/packages/SystemUI/res-keyguard/values-fr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge…"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge rapide…"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge lente"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge en pause pour protéger la batterie"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Vérifier l\'accessoire de recharge"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Aucune SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilisable."</string> diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml index 1163ed00e783..12b6c2cae28e 100644 --- a/packages/SystemUI/res-keyguard/values-gl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carga púxose en pausa para protexer a batería"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Comproba o accesorio de carga"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Non hai ningunha SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"A SIM non se pode usar."</string> diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml index bfbb2f280256..9e2fd536b3e0 100644 --- a/packages/SystemUI/res-keyguard/values-gu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ઝડપથી ચાર્જિંગ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચાર્જિંગ"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • બૅટરીને સુરક્ષિત રાખવા માટે, ચાર્જિંગ હોલ્ડ પર રાખવામાં આવ્યું છે"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ ઍક્સેસરી ચેક કરો"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"નેટવર્ક લૉક થયું"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"કોઈ સિમ કાર્ડ નથી"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ઉપયોગમાં ન લઈ શકાતું સિમ કાર્ડ."</string> diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml index 9ee840de657f..12cb7e396ded 100644 --- a/packages/SystemUI/res-keyguard/values-hi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज हो रहा है"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • तेज़ चार्ज हो रहा है"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • धीरे चार्ज हो रहा है"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • बैटरी को सुरक्षित रखने के लिए, चार्जिंग को रोका गया है"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्जिंग ऐक्सेसरी की जांच करें"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक किया हुआ है"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"कोई सिम नहीं है"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"सिम को हमेशा के लिए बंद कर दिया गया है."</string> diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml index 4edd7d2fae2d..fad222db3172 100644 --- a/packages/SystemUI/res-keyguard/values-hr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • punjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • brzo punjenje"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • sporo punjenje"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je pauzirano radi zaštite baterije"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Provjerite dodatak za punjenje"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM je neupotrebljiv."</string> diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml index f27156d4ea3f..a22fbceb8bcf 100644 --- a/packages/SystemUI/res-keyguard/values-hu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Töltés"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Gyors töltés"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lassú töltés"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Az akkumulátor védelme érdekében a töltés szünetel"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ellenőrizze a töltőtartozékot"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Hálózat zárolva"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nincs SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nem használható SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml index a402721ee4fc..119928a43766 100644 --- a/packages/SystemUI/res-keyguard/values-hy/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու համար"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ստուգեք լիցքավորիչը"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ցանցը կողպված է"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM քարտ չկա"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Անվավեր SIM քարտ։"</string> diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml index 229419efa250..f4db0353a840 100644 --- a/packages/SystemUI/res-keyguard/values-in/strings.xml +++ b/packages/SystemUI/res-keyguard/values-in/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan cepat"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengisian daya dihentikan sementara untuk melindungi baterai"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Periksa aksesori pengisi daya"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Jaringan terkunci"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Tidak ada SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM tidak dapat digunakan."</string> diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml index 7edcdbd109b9..c364f60329eb 100644 --- a/packages/SystemUI/res-keyguard/values-is/strings.xml +++ b/packages/SystemUI/res-keyguard/values-is/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hleðsla í bið til að vernda rafhlöðuna"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Athugaðu hleðslubúnaðinn"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ekkert SIM-kort"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ónothæft SIM-kort."</string> diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml index 7856a492936e..659928fc1800 100644 --- a/packages/SystemUI/res-keyguard/values-it/strings.xml +++ b/packages/SystemUI/res-keyguard/values-it/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica veloce"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica in sospeso per proteggere la batteria"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Controlla l\'accessorio di ricarica"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rete bloccata"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nessuna SIM presente"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilizzabile."</string> diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml index 9182c5aaf9d5..0021d0a87e30 100644 --- a/packages/SystemUI/res-keyguard/values-iw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה מהירה"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה איטית"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • הטעינה הושהתה כדי להגן על הסוללה"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • צריך לבדוק את אביזר הטעינה"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"הרשת נעולה"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"אין כרטיס SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"לא ניתן להשתמש בכרטיס ה-SIM הזה."</string> diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml index e589f85cc91e..c94d6b18cc8a 100644 --- a/packages/SystemUI/res-keyguard/values-ja/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 急速充電中"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 低速充電中"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • バッテリーを保護するため、充電を一時停止しています"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電用アクセサリを確認してください"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ネットワークがロックされました"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM がありません"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM が使用できません。"</string> diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml index 0fe5a397466f..c36b4718c292 100644 --- a/packages/SystemUI/res-keyguard/values-ka/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • იტენება"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • სწრაფად იტენება"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ნელა იტენება"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • დატენვა შეჩერებულია ბატარეის დასაცავად"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • შეამოწმეთ დამტენი აქსესუარი"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ქსელი ჩაკეტილია"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM არ არის"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"გამოუყენებელი SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml index 3882c5d1ecac..fef02beea07a 100644 --- a/packages/SystemUI/res-keyguard/values-kk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядтау"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батареяны қорғау мақсатында зарядтау кідіртілді."</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядтау құрылғысын тексеріңіз."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM картасы жоқ."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM картасын пайдалану мүмкін емес."</string> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 0ebda041daec..f82defdc453e 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្ម"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្មយឺត"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងផ្អាកការសាកថ្ម ដើម្បីការពារថ្ម"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ពិនិត្យមើលគ្រឿងសាកថ្ម"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"បណ្ដាញជាប់សោ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"គ្មានស៊ីមទេ"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ស៊ីមមិនអាចប្រើបាន។"</string> diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml index 4dbefed138b9..4ab035e5a420 100644 --- a/packages/SystemUI/res-keyguard/values-kn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಬ್ಯಾಟರಿಯನ್ನು ರಕ್ಷಿಸಲು ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಹೋಲ್ಡ್ ಮಾಡಲಾಗಿದೆ"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜಿಂಗ್ ಪರಿಕರವನ್ನು ಪರಿಶೀಲಿಸಿ"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟ್ವರ್ಕ್ ಲಾಕ್ ಆಗಿದೆ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM ಇಲ್ಲ"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ನಿಷ್ಪ್ರಯೋಜಕವಾಗಿದೆ."</string> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index e0d764502a0c..d4563660b01a 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 충전 중"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 고속 충전 중"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 저속 충전 중"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 배터리 보호를 위해 충전 일시중지"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 충전 액세서리 확인"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"네트워크 잠김"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM 없음"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM을 사용할 수 없습니다."</string> diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index cc777a29ae0d..e8640ae5caa3 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубатталууда"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Тез кубатталууда"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жай кубатталууда"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батареяны коргоо үчүн кубаттоо тындырылды"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубаттоо шайманын текшериңиз"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Тармак кулпуланган"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM карта жок"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Жараксыз SIM карта."</string> diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml index ce2fa3bfe3f2..6f6c1d11915b 100644 --- a/packages/SystemUI/res-keyguard/values-lo/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບດ່ວນ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບຊ້າ"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ຢຸດການສາກຊົ່ວຄາວເພື່ອປົກປ້ອງແບັດເຕີຣີ"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກວດສອບອຸປະກອນເສີມສຳລັບການສາກ"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ເຄືອຂ່າຍຖືກລັອກ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ບໍ່ມີຊິມ"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ຊິມໃຊ້ບໍ່ໄດ້."</string> diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml index 4344b61c2375..9d134853400f 100644 --- a/packages/SystemUI/res-keyguard/values-lt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Įkraunama"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Greitai įkraunama"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lėtai įkraunama"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • įkrovimas pristabdytas, siekiant apsaugoti akumuliatorių"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • patikrinkite įkrovimo priedą"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tinklas užrakintas"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nėra SIM kortelės"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nenaudojama SIM kortelė."</string> diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml index b5215119e968..10d657ba999a 100644 --- a/packages/SystemUI/res-keyguard/values-lv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek uzlāde"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek ātrā uzlāde"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek lēnā uzlāde"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Uzlāde apturēta, lai aizsargātu akumulatoru"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pārbaudiet uzlādes piederumu"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tīkls ir bloķēts."</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nav SIM kartes"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM karte nav izmantojama."</string> diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml index 7fe966fc76fe..9aafb86c8e27 100644 --- a/packages/SystemUI/res-keyguard/values-mk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо полнење"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бавно полнење"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Полнењето е паузирано за да се заштити батеријата"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проверете го додатокот за полнење"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заклучена"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Нема SIM-картичка"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-картичката е неупотреблива."</string> diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml index afd7c40ada0a..f690ce5b35ae 100644 --- a/packages/SystemUI/res-keyguard/values-ml/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജ് ചെയ്യുന്നു"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • വേഗത്തിൽ ചാർജ് ചെയ്യുന്നു"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ബാറ്ററി പരിരക്ഷിക്കാൻ ചാർജിംഗ് ഹോൾഡിലാണ്"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജിംഗ് ആക്സസറി പരിശോധിക്കുക"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"നെറ്റ്വർക്ക് ലോക്കുചെയ്തു"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"സിം ഇല്ല"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ഉപയോഗശൂന്യമായ സിം."</string> diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml index d29daa560475..70cfa3741451 100644 --- a/packages/SystemUI/res-keyguard/values-mn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарейг хамгаалахын тулд цэнэглэхийг хүлээлгэсэн"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэх нэмэлт хэрэгслийг шалгах"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сүлжээ түгжигдсэн"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM байхгүй"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ашиглах боломжгүй SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml index 85522d7dafc4..9da4fb084c12 100644 --- a/packages/SystemUI/res-keyguard/values-mr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज होत आहे"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वेगाने चार्ज होत आहे"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • सावकाश चार्ज होत आहे"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • बॅटरीचे संरक्षण करण्यासाठी चार्जिंग थांबवले आहे"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्जिंगसंबंधित ॲक्सेसरी तपासा"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक केले"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"सिम नाही"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"वापरण्यायोग्य नसलेले सिम."</string> diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml index 1b8ef075737e..76de213cc206 100644 --- a/packages/SystemUI/res-keyguard/values-ms/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan cepat"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengecasan ditunda untuk melindungi bateri"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Periksa aksesori pengecasan"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rangkaian dikunci"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Tiada SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM tidak boleh digunakan."</string> diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml index a638d17dde3e..bedabb811653 100644 --- a/packages/SystemUI/res-keyguard/values-my/strings.xml +++ b/packages/SystemUI/res-keyguard/values-my/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းနေသည်"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အမြန်အားသွင်းနေသည်"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည်"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ဘက်ထရီကို ကာကွယ်ရန် အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းပစ္စည်းကို စစ်ပါ"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ကွန်ရက်ကို လော့ခ်ချထားသည်"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ဆင်းမ်ကတ် မရှိပါ"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ဆင်းမ်ကတ်ကို သုံး၍မရပါ။"</string> diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml index b289b72cf16a..04b567b29017 100644 --- a/packages/SystemUI/res-keyguard/values-nb/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader raskt"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader sakte"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladingen er satt på vent for å beskytte batteriet"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> · Sjekk ladetilbehøret"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nettverket er låst"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ingen SIM-kort"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-kortet kan ikke brukes."</string> diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml index fc6f21fe11f6..692e693968d5 100644 --- a/packages/SystemUI/res-keyguard/values-ne/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज गरिँदै"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • द्रुत गतिमा चार्ज गरिँदै छ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • मन्द गतिमा चार्ज गरिँदै"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ब्याट्रीको सुरक्षा गर्न चार्जिङ होल्ड गरिएको छ"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्जिङ एक्सेसरी जाँच्नुहोस्"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लक भएको छ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM कार्ड हालिएको छैन"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"यो SIM कार्ड प्रयोग गर्न मिल्दैन।"</string> diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml index 093c7b7f51e8..9833505e49a7 100644 --- a/packages/SystemUI/res-keyguard/values-nl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Snel opladen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Langzaam opladen"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen in de wacht gezet om batterij te beschermen"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Check je oplaadaccessoire"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk vergrendeld"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Geen simkaart"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Onbruikbare simkaart."</string> diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml index 76dbdaf33357..5ed2f11ddf99 100644 --- a/packages/SystemUI/res-keyguard/values-or/strings.xml +++ b/packages/SystemUI/res-keyguard/values-or/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜ ହେଉଛି"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଦ୍ରୁତ ଭାବେ ଚାର୍ଜ ହେଉଛି"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ବେଟେରୀକୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜିଂ ଆକସେସୋରୀକୁ ଯାଞ୍ଚ କରନ୍ତୁ"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ନେଟୱର୍କକୁ ଲକ୍ କରାଯାଇଛି"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"କୌଣସି SIM ନାହିଁ"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ବ୍ୟବହାର ଅଯୋଗ୍ୟ ଥିବା SIM।"</string> diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml index 10e9eeff055f..e591e5d2f724 100644 --- a/packages/SystemUI/res-keyguard/values-pa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜਿੰਗ ਐਕਸੈਸਰੀ ਦੀ ਜਾਂਚ ਕਰੋ"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ਕੋਈ ਸਿਮ ਨਹੀਂ ਹੈ"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ਬੇਕਾਰ ਸਿਮ।"</string> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index 08cbc29d3995..396fa7953f29 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> – wstrzymano ładowanie, aby chronić baterię"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> – sprawdź akcesoria do ładowania"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Brak karty SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nie można użyć karty SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml index efd1c75d6cce..3de54f92cfc6 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento suspenso para proteger a bateria"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Verifique o acessório de carregamento"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem chip"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Chip inutilizável."</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index 38cc437e332c..3d8f7e6ccab1 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar…"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar rapidamente…"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar lentamente…"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento em espera para proteger a bateria"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Verificar acessório de carregamento"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilizável."</string> diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml index efd1c75d6cce..3de54f92cfc6 100644 --- a/packages/SystemUI/res-keyguard/values-pt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento suspenso para proteger a bateria"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Verifique o acessório de carregamento"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem chip"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Chip inutilizável."</string> diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml index e3cfd3ee9ac9..053f862f0ba2 100644 --- a/packages/SystemUI/res-keyguard/values-ro/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă rapid"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă lent"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Încărcarea s-a întrerupt pentru a proteja bateria"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Verifică accesoriul de încărcare"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rețea blocată"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Niciun card SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Cardul SIM nu se poate folosi."</string> diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml index b5b326632dc7..6be148934f9c 100644 --- a/packages/SystemUI/res-keyguard/values-ru/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"Идет зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"Идет быстрая зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"Идет медленная зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядка приостановлена для защиты батареи."</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проверьте зарядное устройство."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сеть заблокирована"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM-карта отсутствует"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-карту невозможно использовать."</string> diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml index ecd3f3f9afa2..401e147b21a2 100644 --- a/packages/SystemUI/res-keyguard/values-si/strings.xml +++ b/packages/SystemUI/res-keyguard/values-si/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බැටරිය ආරක්ෂා කිරීම සඳහා ආරෝපණය රඳවා තබා ඇත"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණ ආයිත්තම පරීක්ෂා කරන්න"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ජාලය අගුළු දමා ඇත"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM නැත"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"භාවිත කළ නොහැකි SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml index c38ba64789e3..2b65466f7cb4 100644 --- a/packages/SystemUI/res-keyguard/values-sk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa rýchlo"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa pomaly"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjanie je pozastavené, aby sa chránila batéria"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Skontrolujte nabíjacie príslušenstvo"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieť je zablokovaná"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Žiadna SIM karta"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nepoužiteľná SIM karta."</string> diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml index c066f50b0132..dbb34c953637 100644 --- a/packages/SystemUI/res-keyguard/values-sl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Zaradi zaščite baterije je polnjenje na čakanju"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Preverite pripomoček za polnjenje"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ni kartice SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kartica SIM je neuporabna."</string> diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml index 1dc93fd1b824..00c4999ecef6 100644 --- a/packages/SystemUI/res-keyguard/values-sq/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet me shpejtësi"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet ngadalë"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Karikimi është vendosur në pritje për të mbrojtur baterinë"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kontrollo aksesorin e karikimit"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rrjeti është i kyçur"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nuk ka kartë SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kartë SIM e papërdorshme."</string> diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml index 34cb06b07b31..feac58330716 100644 --- a/packages/SystemUI/res-keyguard/values-sr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуњење је на чекању да би се заштитила батерија"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проверите додатну опрему за пуњење"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Нема SIM-а"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Неупотребљив SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml index 76c5dca8490d..67fa2f70199b 100644 --- a/packages/SystemUI/res-keyguard/values-sv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas snabbt"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas långsamt"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddningen har pausats för att skydda batteriet"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kontrollera laddningstillbehöret"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nätverk låst"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Inget SIM-kort"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-kortet går inte att använda."</string> diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml index b310ab9ee47a..527e73eeefc0 100644 --- a/packages/SystemUI/res-keyguard/values-sw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji kwa kasi"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji pole pole"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Imesitisha kuchaji ili kulinda betri yako"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kagua kifaa cha kuchaji"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mtandao umefungwa"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Hakuna SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM haiwezi kutumika."</string> diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml index f31137aade62..05ac0c0b3bb9 100644 --- a/packages/SystemUI/res-keyguard/values-ta/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாகிறது"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • வேகமாகச் சார்ஜாகிறது"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • மெதுவாகச் சார்ஜாகிறது"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • பேட்டரியைப் பாதுகாப்பதற்காகச் சார்ஜிங் ஹோல்டு செய்யப்பட்டுள்ளது"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜிங் துணைக்கருவியைச் சரிபார்க்கவும்"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"நெட்வொர்க் பூட்டப்பட்டது"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"சிம் இல்லை"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"பயன்படுத்த முடியாத சிம்."</string> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index 924678f6f32e..9d37fc14b80d 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జ్ అవుతోంది"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • బ్యాటరీని రక్షించడానికి ఛార్జింగ్ హోల్డ్లో ఉంచబడింది"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జింగ్ యాక్సెసరీని చెక్ చేయండి"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్వర్క్ లాక్ చేయబడింది"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM లేదు"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"వినియోగించలేని SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index 980ed9994d73..57aadea4ab56 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างเร็ว"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างช้าๆ"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • หยุดการชาร์จชั่วคราวเพื่อถนอมแบตเตอรี่"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ตรวจสอบอุปกรณ์เสริมสำหรับการชาร์จ"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"เครือข่ายถูกล็อก"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ไม่มี SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ใช้งานไม่ได้"</string> diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml index b054b1ec2c02..bb7c34467677 100644 --- a/packages/SystemUI/res-keyguard/values-tl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nagcha-charge"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabilis na nagcha-charge"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabagal na nagcha-charge"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Naka-hold ang pag-charge para protektahan ang baterya"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Suriin ang accessory sa pag-charge"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Naka-lock ang network"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Walang SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Hindi magagamit na SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml index 343cea1851b7..f4016299fa09 100644 --- a/packages/SystemUI/res-keyguard/values-tr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj oluyor"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hızlı şarj oluyor"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş şarj oluyor"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pili korumak için şarj beklemede"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj aksesuarını kontrol edin"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ağ kilitli"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM yok"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kullanılamayan SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml index 56ca79d405dc..4330b32bc005 100644 --- a/packages/SystemUI/res-keyguard/values-uk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання призупинено, щоб захистити акумулятор"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Перевірте зарядний пристрій"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Немає SIM-карти"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Непридатна SIM-карта."</string> diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml index 3c47dd59b977..e466807c6f53 100644 --- a/packages/SystemUI/res-keyguard/values-ur/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارج ہو رہا ہے"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تیزی سے چارج ہو رہا ہے"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آہستہ چارج ہو رہا ہے"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • بیٹری کی حفاظت کرنے کے لیے چارجنگ ہولڈ پر ہے"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارجنگ ایکسیسری چیک کریں"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"نیٹ ورک مقفل ہو گیا"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"کوئی SIM نہیں ہے"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ناقابل استعمال SIM۔"</string> diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml index f58628b6b706..f3cdf26be5c4 100644 --- a/packages/SystemUI/res-keyguard/values-uz/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvat olmoqda"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tezkor quvvat olmoqda"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareyani himoyalash uchun quvvatlash toʻxtatildi"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvatlash aksessuarini tekshiring"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tarmoq qulflangan"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM kartasiz"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ishlamaydigan SIM."</string> diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml index 7fb272f0dc0e..3bad8f1dec4e 100644 --- a/packages/SystemUI/res-keyguard/values-vi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang tạm ngưng sạc để bảo vệ pin"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hãy kiểm tra phụ kiện sạc"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Không có SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM không sử dụng được."</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml index 79e00fda09d3..2818ffb18673 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充电"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充电"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在慢速充电"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 为保护电池,充电已暂停"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 请检查充电配件"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"网络已锁定"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"没有 SIM 卡"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM 卡无法使用。"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml index 76f366a19d14..1b196d4db25d 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充電"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> - 為保護電池,目前暫停充電"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 檢查充電配件"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網絡已鎖定"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"沒有 SIM 卡"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM 卡無法使用。"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml index e7e25c1a4d42..9b7e4318dc86 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 為保護電池,目前暫停充電"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 請檢查充電配件"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"沒有 SIM 卡"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM 卡無法使用。"</string> diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml index 61f956afceb3..cd36f9596e3a 100644 --- a/packages/SystemUI/res-keyguard/values-zu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml @@ -33,10 +33,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Iyashaja"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kaningi"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kancane"</string> - <!-- no translation found for keyguard_plugged_in_charging_limited (5369697538556777542) --> - <skip /> - <!-- no translation found for keyguard_plugged_in_incompatible_charger (6384203333154532125) --> - <skip /> + <string name="keyguard_plugged_in_charging_limited" msgid="5369697538556777542">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ukushaja kumisiwe ukuze kuvikelwe ibhethri"</string> + <string name="keyguard_plugged_in_incompatible_charger" msgid="6384203333154532125">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hlola insiza yokushaja"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Inethiwekhi ivaliwe"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ayikho i-SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"I-SIM engasebenziseki."</string> diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change_inset.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change_inset.xml new file mode 100644 index 000000000000..02486bfae9b5 --- /dev/null +++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_change_inset.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/accessibility_window_magnification_drag_handle_background_change" + android:insetBottom="@dimen/magnification_inner_border_margin" + android:insetLeft="@dimen/magnification_inner_border_margin" + android:insetRight="@dimen/magnification_inner_border_margin" + android:insetTop="@dimen/magnification_inner_border_margin" /> diff --git a/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_inset.xml b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_inset.xml new file mode 100644 index 000000000000..bfb7c47cb130 --- /dev/null +++ b/packages/SystemUI/res/drawable/accessibility_window_magnification_drag_handle_background_inset.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/accessibility_window_magnification_drag_handle_background" + android:insetBottom="@dimen/magnification_inner_border_margin" + android:insetLeft="@dimen/magnification_inner_border_margin" + android:insetRight="@dimen/magnification_inner_border_margin" + android:insetTop="@dimen/magnification_inner_border_margin" /> diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml index a8a048d8c2f6..6286f343aeff 100644 --- a/packages/SystemUI/res/layout/window_magnifier_view.xml +++ b/packages/SystemUI/res/layout/window_magnifier_view.xml @@ -117,12 +117,11 @@ android:id="@+id/drag_handle" android:layout_width="@dimen/magnification_drag_view_size" android:layout_height="@dimen/magnification_drag_view_size" - android:layout_margin="@dimen/magnification_inner_border_margin" android:layout_gravity="right|bottom" android:padding="@dimen/magnifier_drag_handle_padding" android:scaleType="center" android:src="@drawable/ic_move_magnification" - android:background="@drawable/accessibility_window_magnification_drag_handle_background"/> + android:background="@drawable/accessibility_window_magnification_drag_handle_background_inset"/> <ImageView android:id="@+id/close_button" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 30ac963d9291..32ea2ce0d5bb 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Voeg by, verwyder en herrangskik jou legstukke in hierdie spasie"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Voeg meer legstukke by"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Langdruk om legstukke te pasmaak"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Gebruikerteenwoordigheid is bespeur"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swiep om voort te gaan"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Jou binneste skerm sal weerspieël word. Jou boonste skerm sal afgeskakel word."</string> <string name="mirror_display" msgid="2515262008898122928">"Sinkroniseer skerm wedersyds"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 9aa6a1826515..12bca7d5d883 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"በዚህ ቦታ ላይ የእርስዎን ምግብሮች ያክሉ፣ ያስወግዱ እና እንደገና ቅደም ተከተል ያስይዙ"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ተጨማሪ ምግብሮችን ያክሉ"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ምግብሮችን ለማበጀት በረጅሙ ይጫኑ"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ለመቀጠል ያንሸራትቱ"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ወደ ውጫዊ ማሳያ ይንጸባረቅ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"የውስጥ ማሳያዎ ይንጸባረቃል። የፊት ማሳያዎ ይጠፋል።"</string> <string name="mirror_display" msgid="2515262008898122928">"ማሳያን አንጸባርቅ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 0874ffb2f4da..1101e20feadb 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"يمكنك في هذه المساحة إضافة التطبيقات المصغّرة وإزالتها وإعادة ترتيبها."</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"إضافة المزيد من التطبيقات المصغّرة"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"اضغط مع الاستمرار لتخصيص التطبيقات المصغّرة."</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"مرِّر سريعًا للمتابعة."</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"هل تريد بث محتوى جهازك على الشاشة الخارجية؟"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"سيتم النسخ المطابق لمحتوى الشاشة الداخلية، وإيقاف الشاشة الأمامية."</string> <string name="mirror_display" msgid="2515262008898122928">"بث المحتوى على الشاشة"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 03381880a6b7..3050685015f3 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"এই স্পেচটোত আপোনাৰ ৱিজেটসমূহ যোগ দিয়ক, আঁতৰাওক আৰু সেইসমূহৰ ক্ৰম সলনি কৰক"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"অধিক ৱিজেট যোগ দিয়ক"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ৱিজেট কাষ্টমাইজ কৰিবলৈ দীঘলীয়াকৈ টিপক"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"অব্যাহত ৰাখিবলৈ ছোৱাইপ কৰক"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"বাহ্যিক ডিছপ্লে’লৈ মিৰ’ৰ কৰিবনে?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"আপোনাৰ ইনাৰ ডিছপ্লে’ প্ৰতিবিম্বিত কৰা হ’ব। আপোনাৰ ফ্ৰণ্ট ডিছপ্লে’ অফ কৰা হ’ব।"</string> <string name="mirror_display" msgid="2515262008898122928">"ডিছপ্লে’ মিৰ’ৰ কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index c858c3b70ee1..3c3462b9ea5b 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Burada vidcetlər əlavə edin, silin və sırasını dəyişin"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Vidcetlər əlavə edin"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Basıb saxlayaraq vidcetləri fərdiləşdirin"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"İstifadəçi mövcudluğu aşkarlandı"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Çəkərək davam edin"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Xarici displeyə əks etdirilsin?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"İç displey əks etdiriləcək. Ön ekran deaktiv ediləcək."</string> <string name="mirror_display" msgid="2515262008898122928">"Displeyi əks etdirin"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 379c68eac184..33e7921ef541 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodajte, uklonite i preuredite vidžete u ovom prostoru"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte još vidžeta"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dugi pritisak za prilagođavanje vidžeta"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Prisustvo korisnika može da se otkrije"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Prevucite da biste nastavili"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Unutrašnji ekran će se preslikati. Prednji ekran će se isključiti."</string> <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 97e8f17f9279..b7e3b1bf8dfe 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Дадаць ці выдаліць віджэты ў гэтай вобласці або змяніць іх парадак"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Дадаць іншыя віджэты"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Доўга націскайце, каб наладзіць віджэты"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Правядзіце пальцам, каб працягнуць"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Адлюстраваць на знешнім дысплэі?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Будзе ўключана дубліраванне ўнутранага дысплэя. Пярэдні дысплэй будзе выключаны."</string> <string name="mirror_display" msgid="2515262008898122928">"Адлюстраваць дысплэй"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index f8585bca7616..c1a27d27bfb9 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Добавяйте, премахвайте и пренареждайте приспособленията си в тази област"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Добавете още приспособления"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Натиснете продължително за персонализ. на приспос."</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Прекарайте пръст, за да продължите"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се дублира ли на външния екран?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Съдържанието на вътрешния ви дисплей ще бъде дублирано. Предният ви дисплей ще бъде изключен."</string> <string name="mirror_display" msgid="2515262008898122928">"Дублиране на дисплея"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 2229e8fc0817..a694f5e96c14 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"এই স্পেসে আপনার উইজেট যোগ করুন, সরান ও আবার সাজান"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"আরও উইজেট যোগ করুন"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"উইজেট কাস্টমাইজ করতে বেশিক্ষণ প্রেস করুন"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"চালিয়ে যেতে সোয়াইপ করুন"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"এক্সটার্নাল ডিসপ্লেতে মিরর করবেন?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"আপনার ইনার ডিসপ্লে মিরর করা হবে। আপনার ফ্রন্ট ডিসপ্লে বন্ধ করা হবে।"</string> <string name="mirror_display" msgid="2515262008898122928">"ডিসপ্লে দেখান"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index d704b0ac5663..1c7198a90d74 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodajte, uklonite i promijenite raspored vidžeta u ovom prostoru"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte još vidžeta"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pritisnite i zadržite da prilagodite vidžete"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Otkriveno je prisustvo korisnika"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Prevucite da nastavite"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Preslikati na vanjski ekran?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Unutrašnji ekran će se preslikavati. Prednji ekran će se isključiti."</string> <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index ab3bc8f9ccad..948fb5c9f48d 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Afegeix, suprimeix i reordena widgets en aquest espai"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Afegeix més widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén premut per personalitzar els widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"S\'ha detectat la presència d\'usuaris"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Llisca per continuar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Duplicar a la pantalla externa?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"La pantalla interior es duplicarà. La pantalla frontal es desactivarà."</string> <string name="mirror_display" msgid="2515262008898122928">"Duplica la pantalla"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 4dd9d4fb5c69..33397fe0a6f9 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"V tomto prostoru můžete přidávat a odstraňovat widgety a měnit jejich pořadí"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Přidat další widgety"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dlouhým stisknutím můžete přizpůsobit widgety"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Je zjištěna přítomnost uživatele"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Pokračujte přejetím prstem"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Vnitřní displej bude zrcadlen. Přední displej bude vypnutý."</string> <string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index a2d17dfbac52..35142dadde24 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Tilføj, fjern og omorganiser widgets i dette rum"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Tilføj flere widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Hold fingeren nede for at tilpasse widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Brugertilstedeværelse er registreret"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Stryg for at fortsætte"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Din indre skærm spejles. Din skærm på forsiden slukkes."</string> <string name="mirror_display" msgid="2515262008898122928">"Spejl skærm"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index bebedcebdc24..e100b9088f6b 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Hier kannst du Widgets hinzufügen, entfernen und neu anordnen"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Weitere Widgets hinzufügen"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Lange drücken, um Widgets anzupassen"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Anwesenheit des Nutzers wurde erkannt"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Zum Fortfahren wischen"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Auf externen Bildschirm spiegeln?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Dein inneres Display wird gespiegelt. Das Frontdisplay wird ausgeschaltet."</string> <string name="mirror_display" msgid="2515262008898122928">"Bildschirm spiegeln"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 3c87ad90858e..ed3a678fb9ff 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Προσθήκη, κατάργηση και αναδιάταξη των γραφικών στοιχείων σε αυτόν τον χώρο"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Προσθήκη περισσότερων γραφικών στοιχείων"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Παρατεταμένο πάτημα για προσαρμογή γραφ. στοιχείων"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Σύρετε για συνέχεια"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Κατοπτρισμός σε εξωτερική οθόνη;"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Θα γίνει κατοπτρισμός της εσωτερικής προβολής. Η μπροστινή οθόνη θα απενεργοποιηθεί."</string> <string name="mirror_display" msgid="2515262008898122928">"Κατοπτρισμός οθόνης"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index cbdc26fbe012..31b4b6e2f761 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Add, remove and reorder your widgets in this space"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <skip /> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Done"</string> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"User presence is detected"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swipe to continue"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string> <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index da14f53675c7..23cd700af53a 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -418,6 +418,9 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Add, remove, and reorder your widgets in this space"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customize widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Done"</string> @@ -1213,7 +1216,7 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"User presence is detected"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swipe to continue"</string> + <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Swipe up to continue"</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="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string> <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index cbdc26fbe012..31b4b6e2f761 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Add, remove and reorder your widgets in this space"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <skip /> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Done"</string> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"User presence is detected"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swipe to continue"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string> <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index cbdc26fbe012..31b4b6e2f761 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Add, remove and reorder your widgets in this space"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customise widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <skip /> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Done"</string> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"User presence is detected"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swipe to continue"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string> <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index aec29d8c4a1b..29708800bebc 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -418,6 +418,9 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Add, remove, and reorder your widgets in this space"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Add more widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Long press to customize widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <string name="edit_widget" msgid="9030848101135393954">"Edit widget"</string> <string name="button_to_remove_widget" msgid="3948204829181214098">"Remove"</string> <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Add widget"</string> <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Done"</string> @@ -1213,7 +1216,7 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"User presence is detected"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swipe to continue"</string> + <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Swipe up to continue"</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="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string> <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 6dcc40a11d33..8f2d0e74e423 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Agrega, quita y reordena tus widgets en este espacio"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Agregar más widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén presionado para personalizar los widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Se detectó la presencia del usuario"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Desliza el dedo para continuar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Se duplicará la pantalla interior. Se apagará la pantalla frontal."</string> <string name="mirror_display" msgid="2515262008898122928">"Duplicar pantalla"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index aecf5b1f0c44..7848e875f453 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Añade, elimina y reordena tus widgets en este espacio"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Añade más widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén pulsado para personalizar los widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Se ha detectado la presencia de usuarios"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Desliza para continuar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Proyectar a pantalla externa?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Se proyectará tu pantalla interior. Se apagará tu pantalla frontal."</string> <string name="mirror_display" msgid="2515262008898122928">"Proyectar pantalla"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index edfb5bebf2fd..6591799f64c6 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Lisage ja eemaldage selles ruumis oma vidinaid ning muutke nende järjestust"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lisage rohkem vidinaid"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Vajutage pikalt vidinate kohandamiseks"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Tuvastati kasutaja kohalolu"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Pühkige jätkamiseks"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kas peegeldada välisekraanile?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Teie siseekraani peegeldatakse. Teie esiekraan lülitatakse välja."</string> <string name="mirror_display" msgid="2515262008898122928">"Peegelda ekraani"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 7fe37b46e9c5..937aa247eb73 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -366,8 +366,8 @@ <string name="sensor_privacy_mic_unblocked_dialog_content" msgid="4889961886199270224">"Aplikazio eta zerbitzu guztiek mikrofonoa erabil dezakete."</string> <string name="sensor_privacy_mic_blocked_no_exception_dialog_content" msgid="5864898470772965394">"Mikrofonoa erabiltzeko baimena desgaituta dago aplikazio eta zerbitzu guztietarako. Mikrofonoa erabiltzeko baimena gaitzeko, joan Ezarpenak > Pribatutasuna > Mikrofonoa atalera."</string> <string name="sensor_privacy_mic_blocked_with_exception_dialog_content" msgid="810289713700437896">"Mikrofonoa erabiltzeko baimena desgaituta dago aplikazio eta zerbitzu guztietarako. Baimen hori aldatzeko, joan Ezarpenak > Pribatutasuna > Mikrofonoa atalera."</string> - <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Piztu da kamera"</string> - <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Itzali da kamera"</string> + <string name="sensor_privacy_camera_turned_on_dialog_title" msgid="8039095295100075952">"Aktibatu da kamera"</string> + <string name="sensor_privacy_camera_turned_off_dialog_title" msgid="1936603903120742696">"Desaktibatu da kamera"</string> <string name="sensor_privacy_camera_unblocked_dialog_content" msgid="7847190103011782278">"Aplikazio eta zerbitzu guztiek kamera erabil dezakete."</string> <string name="sensor_privacy_camera_blocked_dialog_content" msgid="3182428709314874616">"Kamera erabiltzeko baimena desgaituta dago aplikazio eta zerbitzu guztietarako."</string> <string name="sensor_privacy_htt_blocked_dialog_content" msgid="3333321592997666441">"Mikrofonoaren botoia erabiltzeko, gaitu mikrofonoa erabiltzeko baimena ezarpenetan."</string> @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Gehitu eta kendu eremu honetako widgetak, edo aldatu haien ordena"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Gehitu widget gehiago"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widgetak pertsonalizatzeko, sakatu luze"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Erabiltzailearen presentzia hauteman da"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Aurrera egiteko, pasatu hatza"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Barneko pantaila islatuko da. Aurreko pantaila desaktibatu egingo da."</string> <string name="mirror_display" msgid="2515262008898122928">"Islatu pantaila"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 485009d1d6bc..9948f5f535ec 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"افزودن، برداشتن، و تغییر ترتیب ابزارکها در این فضا"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"افزودن ابزارکهای بیشتر"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"برای سفارشیسازی ابزارکها، فشار طولانی دهید"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"برای ادامه، تند بکشید"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"روی نمایشگر خارجی قرینهسازی شود؟"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"نمایشگر داخلی شما قرینهسازی میشود. نمایشگر جلو خاموش میشود."</string> <string name="mirror_display" msgid="2515262008898122928">"قرینهسازی نمایشگر"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index e65d7e07f036..d4bb6bd2df1b 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Lisää, poista ja järjestä widgetejäsi uudelleen tässä tilassa"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lisää widgetejä"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Yksilöi widgetit pitkällä painalluksella"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Käyttäjän läsnäolo havaittu"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Jatka pyyhkäisemällä"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Sisänäyttö peilataan. Etunäyttö laitetaan pois päältä."</string> <string name="mirror_display" msgid="2515262008898122928">"Peilaa näyttö"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 557b9b8bbe13..ea7b0b0cf1fe 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Ajouter, retirer et réorganiser vos widgets dans cet espace"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ajouter plus de widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Maintenez le doigt pour personnaliser les widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"La présence d\'un utilisateur est détectée"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Balayez l\'écran pour continuer"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer l\'écran sur un moniteur externe?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Votre écran intérieur sera dupliqué. Votre écran frontal sera désactivé."</string> <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 13af9a51c1f8..acaa43e28d53 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dans cet espace, ajoutez, supprimez et réorganisez vos widgets"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ajouter des widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Appuyez de manière prolongée pour personnaliser les widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"La présence de l\'utilisateur est détectée"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Balayer pour continuer"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Votre écran intérieur sera dupliqué. Votre écran frontal sera éteint."</string> <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index d30770d08e26..6b238688bf40 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Engadir, quitar e reordenar widgets neste espazo"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Engadir máis widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pulsación longa para personalizar os widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Detectouse a presenza de usuarios"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Pasa o dedo para continuar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Proxectarase a pantalla interior. Desactivarase a pantalla frontal."</string> <string name="mirror_display" msgid="2515262008898122928">"Proxectar pantalla"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 62ffdd3c93a6..6ea1f0e2e2d9 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"આ સ્પેસમાં તમારા વિજેટ ઉમેરો, તેને કાઢી નાખો અને ફરી તેને ક્રમમાં ગોઠવો"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"વધુ વિજેટ ઉમેરો"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"વિજેટ કસ્ટમાઇઝ કરવા માટે થોડીવાર દબાવી રાખો"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ચાલુ રાખવા સ્વાઇપ કરો"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"શું બાહ્ય ડિસ્પ્લે પર મિરર કરીએ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"તમારું ઇનર ડિસ્પ્લે મિરર કરવામાં આવશે. તમારું ફ્રન્ટ ડિસ્પ્લે બંધ કરવામાં આવશે."</string> <string name="mirror_display" msgid="2515262008898122928">"મિરર ડિસ્પ્લે"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 65cf5f790544..db3549a599be 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"इस स्पेस में विजेट जोड़ें, हटाएं, और उन्हें फिर से क्रम में लगाएं"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ज़्यादा विजेट जोड़ें"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट पसंद के मुताबिक बनाने के लिए उसे दबाकर रखें"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"जारी रखने के लिए स्वाइप करें"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"क्या आपको किसी बाहरी डिवाइस पर डिसप्ले करना है?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"आपके फ़ोन के इनर डिसप्ले की स्क्रीन शेयर की जाएगी. फ़्रंट डिसप्ले को बंद कर दिया जाएगा."</string> <string name="mirror_display" msgid="2515262008898122928">"डिसप्ले करें"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index b502e85c1873..27eb8e9fe972 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodavanje, uklanjanje i promjena redoslijeda widgeta u ovom prostoru"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodavanje još widgeta"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dugo pritisnite za prilagodbu widgeta"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Otkrivena je prisutnost korisnika"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Prijeđite prstom za nastavak"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Unutarnji zaslon bit će zrcaljen. Prednji zaslon bit će isključen."</string> <string name="mirror_display" msgid="2515262008898122928">"Zrcaljenje zaslona"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index c7ce0b5b4821..7a16d741d553 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Modulok hozzáadása, eltávolítása és átrendezése ezen a területen"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"További modulok hozzáadása"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nyomja meg hosszan a modulok személyre szabásához"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Felhasználói jelenlét észlelve"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Csúsztasson a folytatáshoz"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"A belső kijelző tükrözve lesz. Az elülső kijelző ki lesz kapcsolva."</string> <string name="mirror_display" msgid="2515262008898122928">"Kijelző tükrözése"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 7db84dbc4c92..f3e883fda106 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Ավելացնել վիջեթներ, ինչպես նաև հեռացնել և վերադասավորել դրանք այս տարածքում"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ավելացնել վիջեթներ"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Երկար սեղմեք՝ վիջեթները հարմարեցնելու համար"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Սահեցրեք շարունակելու համար"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Հայելապատճենե՞լ արտաքին էկրանին"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ներքին էկրանը կհայելապատճենվի։ Առջևի էկրանը կանջատվի։"</string> <string name="mirror_display" msgid="2515262008898122928">"Հայելապատճենել էկրանը"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index b7e7e8150e77..fdd111fe58bb 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Tambahkan, hapus, dan susun ulang widget Anda di ruang ini"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Tambahkan widget lainnya"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tekan lama untuk menyesuaikan widget"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Kehadiran pengguna terdeteksi"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Geser untuk melanjutkan"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Cerminkan ke layar eksternal?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Layar dalam akan dicerminkan. Layar depan akan dinonaktifkan."</string> <string name="mirror_display" msgid="2515262008898122928">"Cerminkan layar"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index db1b74392cd8..cdbc055cf996 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Bættu við, fjarlægðu og endurraðaðu græjunum þínum í þessu rými"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Bæta við fleiri græjum"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Haltu inni til að sérsníða græjur"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Viðvera notanda greindist"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Strjúktu til að halda áfram"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spegla yfir á ytri skjá?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Innri skjárinn þinn verður speglaður. Slökkt verður á framskjánum þínum."</string> <string name="mirror_display" msgid="2515262008898122928">"Spegla skjá"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 997b76b7ae3b..ec74e4081e81 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Aggiungi, rimuovi e riordina i tuoi widget in questo spazio"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Aggiungi altri widget"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Premi a lungo per personalizzare i widget"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Viene rilevata la presenza dell\'utente"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Scorri per continuare"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Verrà eseguito il mirroring del tuo display interno. Il tuo display frontale verrà spento."</string> <string name="mirror_display" msgid="2515262008898122928">"Esegui il mirroring del display"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 22079ac98ee5..ee1d0bd237a8 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"הוספה, הסרה, וסידור מחדש של הווידג\'טים במרחב הזה"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"הוספת ווידג\'טים"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"לוחצים לחיצה ארוכה כדי להתאים אישית את הווידג\'טים"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"צריך להחליק כדי להמשיך"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"לשקף למסך חיצוני?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"המסך הפנימי שלך ישוכפל. המסך החיצוני שלך יכובה."</string> <string name="mirror_display" msgid="2515262008898122928">"תצוגת מראה"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 74ccd50daad7..416a61de8d1a 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"このスペースでのウィジェットの追加、削除、並べ替え"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ウィジェットの追加"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長押ししてウィジェットをカスタマイズ"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"スワイプして続行"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"外部ディスプレイにミラーリングしますか?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"インナー ディスプレイがミラーリングされます。フロント ディスプレイはオフになります。"</string> <string name="mirror_display" msgid="2515262008898122928">"ディスプレイをミラーリングする"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 59d7487293b9..db57e7e87ed2 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ამ სივრცეში შეძლებთ თქვენი ვიჯეტების დამატებას, ამოშლასა და გადაწყობას"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ვიჯეტების დამატება"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ხანგრძლივად დააჭირეთ ვიჯეტების მოსარგებად"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"გადაფურცლეთ გასაგრძელებლად"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"აირეკლოს გარე ეკრანზე?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"თქვენი შიდა ეკრანი აირეკლება. თქვენი წინა ეკრანი გამოირთვება."</string> <string name="mirror_display" msgid="2515262008898122928">"ეკრანის არეკვლა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index b6a18d79a395..a90c6e82112f 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Осы бөлмеде виджеттер қосыңыз, оларды өшіріңіз және ретін өзгертіңіз."</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Басқа виджеттер қосыңыз."</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерді бейімдеу үшін ұзақ басып тұрыңыз."</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Жалғастыру үшін сырғытыңыз."</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Сыртқы экран арқылы да көрсету керек пе?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ішкі экран көшірмесі көрсетіледі. Алдыңғы экран өшіріледі."</string> <string name="mirror_display" msgid="2515262008898122928">"Көрсету"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index c2551e7bce54..dfa26a5adf5a 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"បញ្ចូល ដកចេញ និងតម្រៀបធាតុក្រាហ្វិករបស់អ្នកឡើងវិញនៅក្នុងលំហនេះ"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"បញ្ចូលធាតុក្រាហ្វិកច្រើនទៀត"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ចុចឱ្យយូរ ដើម្បីប្ដូរធាតុក្រាហ្វិកតាមបំណង"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"សូមអូសដើម្បីបន្ត"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"បញ្ចាំងទៅផ្ទាំងអេក្រង់ខាងក្រៅឬ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"អេក្រង់ខាងក្នុងរបស់អ្នកនឹងត្រូវបានធ្វើសមកាលកម្មទៅវិញទៅមក។ អេក្រង់មុខរបស់អ្នកនឹងត្រូវបានបិទ។"</string> <string name="mirror_display" msgid="2515262008898122928">"បញ្ចាំងទៅផ្ទាំងអេក្រង់"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index a04c95b8166f..51d74cc4de8d 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ಈ ಜಾಗದಲ್ಲಿ ನಿಮ್ಮ ವಿಜೆಟ್ಗಳನ್ನು ಸೇರಿಸಿ, ತೆಗೆದುಹಾಕಿ ಮತ್ತು ಮರುಕ್ರಮಗೊಳಿಸಿ"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ಹೆಚ್ಚಿನ ವಿಜೆಟ್ಗಳನ್ನು ಸೇರಿಸಿ"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ವಿಜೆಟ್ಗಳನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ದೀರ್ಘಕಾಲ ಒತ್ತಿರಿ"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ಮುಂದುವರಿಯಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ಬಾಹ್ಯ ಡಿಸ್ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಬೇಕೆ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ನಿಮ್ಮ ಒಳಗಿನ ಡಿಸ್ಪ್ಲೇ ಅನ್ನು ಪ್ರತಿಬಿಂಬಿಸಲಾಗುತ್ತದೆ. ನಿಮ್ಮ ಫ್ರಂಟ್ ಡಿಸ್ಪ್ಲೇ ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗುತ್ತದೆ."</string> <string name="mirror_display" msgid="2515262008898122928">"ಮಿರರ್ ಡಿಸ್ಪ್ಲೇ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index e199783ca050..316fea7f590a 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"이 공간에서 위젯 추가, 삭제 또는 다시 정렬"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"더 많은 위젯 추가"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"위젯을 맞춤설정하려면 길게 누르기"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"계속하려면 스와이프하세요"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"외부 디스플레이로 미러링하시겠습니까?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"내부 디스플레이가 미러링됩니다. 전면 디스플레이는 꺼집니다."</string> <string name="mirror_display" msgid="2515262008898122928">"디스플레이 미러링"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index e42dc71f539f..d7b13a6c307c 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Бул иш чөйрөсүнө виджеттерди кошуп, өчүрүп жана иретин өзгөртүңүз"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Көбүрөөк виджеттерди кошуу"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерди ыңгайлаштыруу үчүн кое бербей басып туруңуз"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Улантуу үчүн экранды сүрүп коюңуз"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Тышкы экранга чыгарасызбы?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ички экраныңыз башка экранга чыгарылат. Алдыңкы экраныңыз өчүрүлөт."</string> <string name="mirror_display" msgid="2515262008898122928">"Тышкы экран"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index ea12242d3684..1f0fbe425913 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ເພີ່ມ, ລຶບອອກ ແລະ ຈັດຮຽງວິດເຈັດຂອງທ່ານຄືນໃໝ່ໃນພື້ນທີ່ນີ້"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ເພີ່ມວິດເຈັດເພີ່ມເຕີມ"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ກົດຄ້າງໄວ້ເພື່ອປັບແຕ່ງວິດເຈັດ"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ປັດເພື່ອສືບຕໍ່"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ສາຍໃສ່ຈໍສະແດງຜົນພາຍນອກບໍ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ການສະແດງຜົນທາງໃນຂອງທ່ານຈະຖືກສະທ້ອນ. ການສະແດງຜົນທາງໜ້າຂອງທ່ານຈະຖືກປິດໄວ້."</string> <string name="mirror_display" msgid="2515262008898122928">"ຈໍສະແດງຜົນແບບສະທ້ອນ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 38a25e5a95e9..df88f0a3d3c4 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Šioje erdvėje pridėkite valdiklių, juos pašalinkite ir keiskite jų tvarką"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Pridėkite daugiau valdiklių"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Ilgai paspauskite, kad tinkintumėte valdiklius"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Aptikta naudotojo veikla"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Perbraukite, kad galėtumėte tęsti"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Bus bendrinamas vidinio rodinio ekrano vaizdas. Priekinis rodinys bus išjungtas."</string> <string name="mirror_display" msgid="2515262008898122928">"Bendrinti ekrano vaizdą"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index bea683b22f37..7139fdb84cce 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Šajā vietā varat pievienot, noņemt un pārkārtot logrīkus"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Pievienot citus logrīkus"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nospiediet un turiet, lai pielāgotu logrīkus."</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Konstatēta lietotāja klātbūtne"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Velciet, lai turpinātu"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Jūsu iekšējais displejs tiks spoguļots. Jūsu priekšējais displejs tiks izslēgts."</string> <string name="mirror_display" msgid="2515262008898122928">"Spoguļot displeju"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 26192e5a815c..26e4caa1866e 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Додајте, отстранете и прераспоредете ги виџетите во просторов"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додајте повеќе виџети"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Притиснете долго за да ги приспособите виџетите"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Повлечете нагоре за да продолжите"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се преслика на надворешниот екран?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Вашиот внатрешен екран ќе се отслика. Вашиот преден екран ќе се исклучи."</string> <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 8d3a8cc1fb29..181e2fd58f43 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ഈ സ്പെയ്സിൽ നിങ്ങളുടെ വിജറ്റുകൾ ചേർക്കുക, നീക്കം ചെയ്യുക, പുനഃക്രമീകരിക്കുക"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"കൂടുതൽ വിജറ്റുകൾ ചേർക്കുക"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കാൻ ദീർഘനേരം അമർത്തുക"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"തുടരാൻ സ്വൈപ്പ് ചെയ്യുക"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ബാഹ്യ ഡിസ്പ്ലേയിലേക്ക് മിറർ ചെയ്യണോ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"നിങ്ങളുടെ ഇന്നർ ഡിസ്പ്ലേ മിറർ ചെയ്യും. നിങ്ങളുടെ ഫ്രണ്ട് ഡിസ്പ്ലേ ഓഫാകും."</string> <string name="mirror_display" msgid="2515262008898122928">"മിറർ ഡിസ്പ്ലേ"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 3380af485cfe..8b8265dd2515 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Энэ орон зайд виджетүүдээ нэмэх, хасах болон дахин эрэмбэлэх"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Илүү олон виджет нэмэх"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджетүүдийг өөрчлөхийн тулд удаан дарна уу"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Үргэлжлүүлэхийн тулд шударна уу"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Гадны дэлгэцэд тусгал үүсгэх үү?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Таны дотоод дэлгэцийн тусгалыг үүсгэнэ. Таны урд талын дэлгэцийг унтраана."</string> <string name="mirror_display" msgid="2515262008898122928">"Дэлгэцийн тусгал үүсгэх"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 58ceb7936ea1..b61d408a3b3b 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"या स्पेसमध्ये तुमची विजेट जोडा, काढून टाका आणि पुन्हा क्रमाने लावा"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"आणखी विजेट जोडा"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट कस्टमाइझ करण्यासाठी प्रेस करून ठेवा"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"सुरू ठेवण्यासाठी स्वाइप करा"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेवर मिरर करायचे आहे का?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"तुमचा अंतर्गत डिस्प्ले मिरर केला जाईल. तुमचा पुढील डिस्प्ले बंद केला जाईल."</string> <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर करा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 04e017011037..86c0dd93f29d 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Tambahkan, alih keluar dan susun semula widget anda dalam ruang ini"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Tambahkan lagi widget"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tekan lama untuk menyesuaikan widget"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Kehadiran pengguna dikesan"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Leret untuk meneruskan proses"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Paparkan pada paparan luaran?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Paparan dalaman anda akan dicerminkan. Paparan depan anda akan dimatikan."</string> <string name="mirror_display" msgid="2515262008898122928">"Segerakkan paparan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 5b7fa6d1245c..19c9c04e2ccb 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ဤနေရာတွင် သင့်ဝိဂျက်များကို ထည့်ရန်၊ ဖယ်ရှားရန်၊ ပြန်စီရန်"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"နောက်ထပ်ဝိဂျက်များ ထည့်ရန်"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ဝိဂျက်များ စိတ်ကြိုက်လုပ်ရန် ကြာကြာနှိပ်ထားပါ"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ရှေ့ဆက်ရန် ပွတ်ဆွဲပါ"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ပြင်ပဖန်သားပြင်သို့ စကရင်ပွားမလား။"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"အတွင်းပြကွက်ကို စကရင်ပွားပါမည်။ ရှေ့မျက်နှာပြင်ပြကွက်ကို ပိတ်မည်။"</string> <string name="mirror_display" msgid="2515262008898122928">"ဖန်သားပြင်ကို စကရင်ပွားရန်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 1dd377e441d8..18fba667bbbe 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Legg til, fjern og omorganiser modulene i dette området"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Legg til flere moduler"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Trykk lenge for å tilpasse modulene"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Det er registrert at brukeren er til stede"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Sveip for å fortsette"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Den indre skjermen speiles. Den ytre skjermen slås av."</string> <string name="mirror_display" msgid="2515262008898122928">"Speil skjermen"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 58baedea8ea5..a192c93955d7 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"यो स्पेसमा आफ्ना विजेटहरू हाल्नुहोस्, हटाउनुहोस् र तिनका क्रम फेरि मिलाउनुहोस्"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"थप विजेटहरू हाल्नुहोस्"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेटहरू कस्टमाइज गर्न केही बेरसम्म थिच्नुहोस्"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"जारी राख्न स्वाइप गर्नुहोस्"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेमा मिरर गर्ने हो?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"तपाईंको भित्री डिस्प्ले मिरर गरिने छ। तपाईंको अगाडिको डिस्प्ले अफ गरिने छ।"</string> <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index a882a28e46dc..d518a87b3b59 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Je widgets aan deze ruimte toevoegen, eruit verwijderen of opnieuw ordenen"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Meer widgets toevoegen"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Houd lang ingedrukt om widgets aan te passen"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Gebruikersaanwezigheid is waargenomen"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swipe om door te gaan"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spiegelen naar extern scherm?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Het scherm aan de binnenkant wordt gemirrord. Het scherm aan de voorkant wordt uitgezet."</string> <string name="mirror_display" msgid="2515262008898122928">"Scherm spiegelen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 59145d429045..38d0bd97ba76 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ଏହି ସ୍ପେସରେ ଆପଣଙ୍କ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରନ୍ତୁ, କାଢ଼ି ଦିଅନ୍ତୁ ଏବଂ ରିଅର୍ଡର କରନ୍ତୁ"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ଅଧିକ ୱିଜେଟ ଯୋଗ କରନ୍ତୁ"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ୱିଜେଟଗୁଡ଼ିକୁ କଷ୍ଟମାଇଜ କରିବା ପାଇଁ ଅଧିକ ସମୟ ଦବାନ୍ତୁ"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1088,7 +1092,7 @@ <string name="empty_user_name" msgid="3389155775773578300">"ସାଙ୍ଗମାନେ"</string> <string name="empty_status" msgid="5938893404951307749">"ଆଜି ରାତି ଚାଟ କରିବା!"</string> <string name="status_before_loading" msgid="1500477307859631381">"ବିଷୟବସ୍ତୁ ଶୀଘ୍ର ଦେଖାଯିବ"</string> - <string name="missed_call" msgid="4228016077700161689">"ମିସ୍ଡ କଲ୍"</string> + <string name="missed_call" msgid="4228016077700161689">"ମିସ୍ଡ କଲ"</string> <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string> <string name="people_tile_description" msgid="8154966188085545556">"ବର୍ତ୍ତମାନର ମେସେଜ, ମିସ୍ଡ କଲ ଏବଂ ସ୍ଥିତି ଅପଡେଟଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ"</string> <string name="people_tile_title" msgid="6589377493334871272">"ବାର୍ତ୍ତାଳାପ"</string> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ଜାରି ରଖିବାକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେକୁ ମିରର କରିବେ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ଆପଣଙ୍କ ଇନର ଡିସପ୍ଲେକୁ ମିରର କରାଯିବ। ଆପଣଙ୍କ ଫ୍ରଣ୍ଟ ଡିସପ୍ଲେକୁ ବନ୍ଦ କରାଯିବ।"</string> <string name="mirror_display" msgid="2515262008898122928">"ଡିସପ୍ଲେ ମିରର କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index be69c1eab54e..fdbc0e59ca1c 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ਇਸ ਜਗ੍ਹਾ ਵਿੱਚ ਆਪਣੇ ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ, ਹਟਾਓ ਅਤੇ ਮੁੜ-ਕ੍ਰਮਬੱਧ ਕਰੋ"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ਹੋਰ ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ਵਿਜੇਟਾਂ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ਕੀ ਬਾਹਰੀ ਡਿਸਪਲੇ \'ਤੇ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰਨਾ ਹੈ?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ਤੁਹਾਡੀ ਅੰਦਰੂਨੀ ਡਿਸਪਲੇ ਪ੍ਰਤੀਬਿੰਬਤ ਕੀਤੀ ਜਾਵੇਗੀ। ਤੁਹਾਡੀ ਅਗਲੀ ਡਿਸਪਲੇ ਬੰਦ ਕਰ ਦਿੱਤੀ ਜਾਵੇਗੀ।"</string> <string name="mirror_display" msgid="2515262008898122928">"ਡਿਸਪਲੇ ਨੂੰ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 192614b17e09..53a1a67c2686 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodawaj widżety, usuwaj je i zmieniaj ich kolejność w tym obszarze"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodaj więcej widżetów"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Przytrzymaj, aby dostosować widżety"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Wykryto obecność użytkownika"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Przesuń, aby kontynuować"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Powstanie odbicie lustrzane Twojego wewnętrznego ekranu. Przedni ekran zostanie wyłączony."</string> <string name="mirror_display" msgid="2515262008898122928">"Powielaj obraz"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 3ca10d36f9be..350855849baa 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Adicione, remova e reorganize seus widgets neste espaço"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicione mais widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha pressionado para personalizar widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Presença do usuário detectada"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Deslize para continuar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Seu display interno será espelhado. O display frontal será desligado."</string> <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 1656e279aa57..3ac26f0841d4 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Adicionar, remover e reordenar widgets neste espaço"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicionar mais widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha premido para personalizar os widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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">"Concluir"</string> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Quando deteta a presença do utilizador"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Deslize rapidamente para continuar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"O seu ecrã interior vai ser espelhado. O seu ecrã frontal vai ser desativado."</string> <string name="mirror_display" msgid="2515262008898122928">"Espelhar ecrã"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 3ca10d36f9be..350855849baa 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Adicione, remova e reorganize seus widgets neste espaço"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicione mais widgets"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha pressionado para personalizar widgets"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Presença do usuário detectada"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Deslize para continuar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Seu display interno será espelhado. O display frontal será desligado."</string> <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index fa2aa1a3a207..7cdc121d2e70 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Adaugă, elimină și reordonează widgeturile în acest spațiu"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adaugă mai multe widgeturi"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Apasă lung pentru a personaliza widgeturi"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"S-a detectat prezența utilizatorului"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Glisează pentru a continua"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Ecranul interior va fi oglindit. Ecranul frontal va fi dezactivat."</string> <string name="mirror_display" msgid="2515262008898122928">"Afișare în oglindă"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 7cb509fd76cc..a059e5589dbe 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Добавление, удаление и упорядочивание виджетов в этом пространстве"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Добавить виджеты"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Нажмите и удерживайте, чтобы настроить виджеты."</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Проведите по экрану, чтобы продолжить."</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублировать на внешний дисплей?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Для внутреннего экрана включится дублирование. Передний экран будет отключен."</string> <string name="mirror_display" msgid="2515262008898122928">"Дублировать"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 97d1c3de59e9..ad7b241f1842 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"මෙම අවකාශය තුළ ඔබේ විජට් එක් කරන්න, ඉවත් කරන්න, සහ නැවත අනුපිළිවෙල කරන්න"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"තවත් විජට් එක් කරන්න"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"විජට් අභිරුචිකරණය කිරීමට දිගු ඔබන්න"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ඉදිරියට යාමට ස්වයිප් කරන්න"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"බාහිර සංදර්ශකයට දර්පණය කරන්න ද?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ඔබේ අභ්යන්තර සංදර්ශකය පිළිබිඹු වනු ඇත. ඔබේ ඉදිරිපස සංදර්ශකය ක්රියාවිරහිත වනු ඇත."</string> <string name="mirror_display" msgid="2515262008898122928">"සංදර්ශකය දර්පණය කරන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index f1ff09e38cf9..e4162dba455b 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Pridávajte aj odstraňujte miniaplikácie a meňte ich poradie v tomto priestore"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Pridať ďalšie miniaplikácie"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Miniaplikácie prispôsobíte dlhým stlačením"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Bola rozpoznaná prítomnosť používateľa"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Pokračujte potiahnutím"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Vnútorná obrazovka bude zrkadlená. Predná obrazovka bude vypnutá."</string> <string name="mirror_display" msgid="2515262008898122928">"Zrkadliť obrazovku"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 8a9b6240961f..c8f7dc19627d 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodajajte, odstranjujte in prerazporejajte pripomočke v tem prostoru"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte več pripomočkov"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pridržite za prilagajanje pripomočkov"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Zaznana je prisotnost uporabnika"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Povlecite za nadaljevanje"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Notranji zaslon bo zrcaljen. Sprednji zaslon bo izklopljen."</string> <string name="mirror_display" msgid="2515262008898122928">"Zrcali zaslon"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index de68a51d47c4..7dd89a189009 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Shto, hiq dhe rirendit miniaplikacionet e tua në këtë hapësirë"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Shto miniaplikacione të tjera"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Shtyp gjatë për të personalizuar miniaplikacionet"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Është zbuluar prania e përdoruesit"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Rrëshqit shpejt për të vazhduar"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Të pasqyrohet në ekranin e jashtëm?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ekrani i brendshëm do të pasqyrohet. Ekrani i parmë do të çaktivizohet."</string> <string name="mirror_display" msgid="2515262008898122928">"Pasqyro ekranin"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 28ff0abe3295..ca4361770fcf 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Додајте, уклоните и преуредите виџете у овом простору"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додајте још виџета"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Дуги притисак за прилагођавање виџета"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Превуците да бисте наставили"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Желите ли да пресликате на спољњи екран?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Унутрашњи екран ће се пресликати. Предњи екран ће се искључити."</string> <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 8bc214f33cf1..078942d05423 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Lägg till, ta bort och ordna om dina widgetar i det här rummet"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lägg till fler widgetar"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Tryck länge för att anpassa widgetar"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Användarnärvaro har upptäckts"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Svep för att fortsätta"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Den inre skärmen speglas. Den främre skärmen stängs av."</string> <string name="mirror_display" msgid="2515262008898122928">"Spegla skärm"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 2aec42e48752..b056cf7f9001 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Ongeza, ondoa na upange upya wijeti zako katika nafasi hii"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Weka wijeti zingine"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Bonyeza kwa muda mrefu uweke mapendeleo ya wijeti"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Imetambua uwepo wa mtumiaji"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Telezesha kidole ili uendelee"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ungependa kuonyesha kwenye skrini ya nje?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Mwonekano wa ndani wa kifaa chako utaakisiwa. Mwonekano wa mbele wa kifaa chako utazimwa."</string> <string name="mirror_display" msgid="2515262008898122928">"Akisi skrini"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 53190533ec40..42db082dbe70 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"இந்த இடத்தில் உங்கள் விட்ஜெட்களைச் சேர்க்கலாம், அகற்றலாம், மறுவரிசைப்படுத்தலாம்"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"கூடுதல் விட்ஜெட்களைச் சேருங்கள்"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"விட்ஜெட்களைப் பிரத்தியேகமாக்க நீண்ட நேரம் அழுத்துக"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ஸ்வைப் செய்து தொடரலாம்"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"வெளிப்புறக் காட்சிக்கு மிரர் செய்யவா?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"உங்கள் உட்புற டிஸ்பிளே பிரதிபலிக்கப்படும். உங்கள் முன்புற டிஸ்பிளே முடக்கப்படும்."</string> <string name="mirror_display" msgid="2515262008898122928">"டிஸ்பிளேயை மிரர் செய்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index efb8b982c8ef..de866a0ef1a8 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ఈ స్పేస్లో మీ విడ్జెట్లను జోడించండి, తీసివేయండి, క్రమపద్ధతిలో అమర్చండి"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"మరిన్ని విడ్జెట్లను జోడించండి"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"విడ్జెట్లను అనుకూలీకరించడానికి, నొక్కి, ఉంచండి"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"కొనసాగించడానికి స్వైప్ చేయండి"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ఎక్స్టర్నల్ డిస్ప్లేకి మిర్రర్ చేయాలా?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"మీ లోపలి డిస్ప్లే మిర్రర్ చేయబడుతుంది. మీ ముందు వైపు డిస్ప్లే ఆఫ్ చేయబడుతుంది."</string> <string name="mirror_display" msgid="2515262008898122928">"మిర్రర్ డిస్ప్లే"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 78a45d689b17..341a46216cd4 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"เพิ่ม นำออก และจัดลำดับวิดเจ็ตในพื้นที่นี้ใหม่"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"เพิ่มวิดเจ็ตอีก"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"กดค้างเพื่อปรับแต่งวิดเจ็ต"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"ปัดเพื่อทำต่อ"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"มิเรอร์ไปยังจอแสดงผลภายนอกไหม"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ระบบจะมิเรอร์หน้าจอด้านใน และจะปิดหน้าจอด้านหน้า"</string> <string name="mirror_display" msgid="2515262008898122928">"มิเรอร์จอแสดงผล"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 9ede0c73b587..3ac3d3aab056 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Magdagdag, mag-alis, at baguhin ang ayos ng iyong mga widget sa space na ito"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Magdagdag ng higit pang widget"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pindutin nang matagal para i-customize ang mga widget"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Na-detect ang presensya ng user"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Mag-swipe para magpatuloy"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"Imi-mirror ang inner display mo. Io-off ang iyong front display."</string> <string name="mirror_display" msgid="2515262008898122928">"I-mirror ang display"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index ef5d06e52daa..9a0996117769 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Bu alanda widget\'larınızı ekleyin, kaldırın ve yeniden sıralayın"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Daha fazla widget ekle"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widget\'ları özelleştirmek için uzun basın"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Kullanıcı varlığı algılandı"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Devam etmek için kaydırın"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <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="4174707498892447947">"İç ekranınız yansıtılacak. Ön ekranınız kapatılacak."</string> <string name="mirror_display" msgid="2515262008898122928">"Ekranı yansıt"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 700a8a0d08c2..152d93c92cd1 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Додати, вилучити чи впорядкувати віджети в цьому просторі"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додати більше віджетів"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Утримуйте, щоб налаштувати віджети"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Щоб продовжити, проведіть пальцем по екрану"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублювати на зовнішньому екрані?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ваш внутрішній екран буде продубльовано. Передній екран буде вимкнено."</string> <string name="mirror_display" msgid="2515262008898122928">"Дублювати екран"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index a1fab6c54e7b..2ec5007882a2 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"اس اسپیس میں اپنے ویجٹس شامل کریں، ہٹائیں اور دوبارہ ترتیب دیں"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"مزید ویجٹس شامل کریں"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ویجٹس کو حسب ضرورت بنانے کے لیے لانگ پریس کریں"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"جاری رکھنے کے لیے سوائپ کریں"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"بیرونی ڈسپلے پر مرر کریں؟"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"آپ کے اندرونی ڈسپلے کو دو طرفہ مطابقت پذیر بنایا جائے گا۔ آپ کا فرنٹ ڈسپلے آف ہو جائے گا۔"</string> <string name="mirror_display" msgid="2515262008898122928">"ڈسپلے کو مرر کریں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 6478f52ee1ac..8bcda40a77fb 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Bu xonadagi vidjetlaringizni olib tashlang, tartibini oʻzgartiring va yangisini qoʻshing"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Koʻproq vidjetlar qoʻshish"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Vidjetlarni sozlash uchun bosib turing"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Foydalanuvchi aniqlandi"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Davom etish uchun suring"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tashqi displeyda aks ettirilsinmi?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ichki ekran uchun aks ettirish yoqiladi. Old ekran oʻchiriladi."</string> <string name="mirror_display" msgid="2515262008898122928">"Displeyni aks ettirish"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index e42599290e88..51d9c48a8333 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Thêm, xoá và sắp xếp lại các tiện ích trong không gian này."</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Thêm tiện ích khác"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nhấn và giữ để tuỳ chỉnh tiện ích"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Phát hiện thấy người dùng đang hiện diện"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Vuốt để tiếp tục"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Phản chiếu sang màn hình ngoài?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Màn hình trong của bạn sẽ được phản chiếu. Màn hình ngoài của bạn sẽ tắt."</string> <string name="mirror_display" msgid="2515262008898122928">"Phản chiếu màn hình"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 1c6fb412356a..847418a83584 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"在此空间内添加、移除和重新排列您的微件"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"添加更多微件"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"长按即可自定义微件"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"滑动可继续操作"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"镜像到外接显示屏?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"系统将镜像您的内屏,而关闭外屏。"</string> <string name="mirror_display" msgid="2515262008898122928">"镜像到显示屏"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index c6a1bd8f27f5..b5a6089da322 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"增、移除小工具,以及調整小工具在此空間中的位置"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"新增更多小工具"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"輕掃即可繼續瀏覽"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要鏡像投射至外部顯示屏嗎?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"鏡像畫面將顯示在內部螢幕,前方螢幕則會關閉。"</string> <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 5231a06570d6..417b70e87f8c 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"新增、移除小工具,以及調整小工具在這個空間中的位置"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"新增更多小工具"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"滑動畫面繼續瀏覽"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要以鏡像方式投放至外部螢幕嗎?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"鏡像畫面將顯示在內螢幕,封面螢幕則會關閉。"</string> <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 10ee16dfeb28..795ed38013ae 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -418,6 +418,10 @@ <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Engeza, susa, futhi uhlele kabusha amawijethi akho kulesi sikhala"</string> <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Engeza amawijethi engeziwe"</string> <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Cindezela isikhathi eside ukuze wenze ngokwezifiso amawijethi"</string> + <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) --> + <skip /> + <!-- no translation found for edit_widget (9030848101135393954) --> + <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> @@ -1213,7 +1217,8 @@ <string name="assistant_attention_content_description" msgid="4166330881435263596">"Ubukhona bomsebenzisi butholakele"</string> <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="dismissible_keyguard_swipe" msgid="2213369651289613196">"Swayipha ukuze uqhubeke"</string> + <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) --> + <skip /> <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Fanisa nesibonisi sangaphandle?"</string> <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Isibonisi sakho sangaphakathi sizoboniswa. Isibonisi sakho sangaphambili sizovalwa."</string> <string name="mirror_display" msgid="2515262008898122928">"Isibonisi sokufanisa"</string> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 3839dd98cdd3..307a6192a570 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -29,6 +29,9 @@ <color name="status_bar_icons_hover_color_light">#38FFFFFF</color> <!-- 22% white --> <color name="status_bar_icons_hover_color_dark">#38000000</color> <!-- 22% black --> + <!-- The dark background color behind the shade --> + <color name="shade_scrim_background_dark">@*android:color/black</color> + <!-- The color of the background in the separated list of the Global Actions menu --> <color name="global_actions_separated_background">#F5F5F5</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 4209c1f6a732..fa89fcd17797 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1249,7 +1249,7 @@ <dimen name="magnification_drag_corner_margin">8dp</dimen> <dimen name="magnification_frame_move_short">5dp</dimen> <dimen name="magnification_frame_move_long">25dp</dimen> - <dimen name="magnification_drag_view_size">36dp</dimen> + <dimen name="magnification_drag_view_size">70dp</dimen> <dimen name="magnification_controls_size">90dp</dimen> <dimen name="magnification_switch_button_size">56dp</dimen> <dimen name="magnification_switch_button_padding">6dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 47ac96ca7960..79435885f410 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1077,8 +1077,6 @@ <!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] --> <string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string> - <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] --> - <string name="button_to_open_widget_editor">Open the widget editor</string> <!-- Text for CTA button that launches the hub mode widget editor on click. [CHAR LIMIT=50] --> <string name="cta_tile_button_to_open_widget_editor">Customize</string> <!-- Text for CTA button that dismisses the tile on click. [CHAR LIMIT=50] --> @@ -1750,6 +1748,9 @@ <!-- Notification: Snooze panel: Snooze undo button label. [CHAR LIMIT=50]--> <string name="snooze_undo">Undo</string> + <!-- Notification: Snooze panel: Snooze undo content description for a11y. [CHAR LIMIT=NONE]--> + <string name="snooze_undo_content_description">Undo notification snooze</string> + <!-- Notification: Snooze panel: message indicating how long the notification was snoozed for. [CHAR LIMIT=100]--> <string name="snoozed_for_time">Snoozed for <xliff:g id="time_amount" example="15 minutes">%1$s</xliff:g></string> diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt index a00cdfa05726..7c674c8ca9e8 100644 --- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt +++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt @@ -47,3 +47,14 @@ fun Int.toSensorType(): FingerprintSensorType = FingerprintSensorProperties.TYPE_HOME_BUTTON -> FingerprintSensorType.HOME_BUTTON else -> throw IllegalArgumentException("Invalid SensorType value: $this") } + +/** Convert [this] to corresponding [Int] */ +fun FingerprintSensorType.toInt(): Int = + when (this) { + FingerprintSensorType.UNKNOWN -> FingerprintSensorProperties.TYPE_UNKNOWN + FingerprintSensorType.REAR -> FingerprintSensorProperties.TYPE_REAR + FingerprintSensorType.UDFPS_ULTRASONIC -> FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC + FingerprintSensorType.UDFPS_OPTICAL -> FingerprintSensorProperties.TYPE_UDFPS_OPTICAL + FingerprintSensorType.POWER_BUTTON -> FingerprintSensorProperties.TYPE_POWER_BUTTON + FingerprintSensorType.HOME_BUTTON -> FingerprintSensorProperties.TYPE_HOME_BUTTON + } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index f094102ad88f..d4ac19556b0a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -40,7 +40,15 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapApps(TransitionInfo info, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) { - return wrap(info, t, leashMap, new TransitionUtil.LeafTaskFilter()); + // LeafTaskFilter is order-dependent, so the same object needs to be used for all Change + // objects. That's why it's constructed here and captured by the lambda instead of building + // a new one ad hoc every time. + TransitionUtil.LeafTaskFilter taskFilter = new TransitionUtil.LeafTaskFilter(); + return wrap(info, t, leashMap, (change) -> { + // Intra-task activity -> activity transitions should be categorized as apps. + if (change.getActivityComponent() != null) return true; + return taskFilter.test(change); + }); } /** @@ -53,8 +61,12 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapNonApps(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) { - return wrap(info, t, leashMap, (change) -> (wallpapers - ? TransitionUtil.isWallpaper(change) : TransitionUtil.isNonApp(change))); + return wrap(info, t, leashMap, (change) -> { + // Intra-task activity -> activity transitions should be categorized as apps. + if (change.getActivityComponent() != null) return false; + return wallpapers + ? TransitionUtil.isWallpaper(change) : TransitionUtil.isNonApp(change); + }); } private static RemoteAnimationTarget[] wrap(TransitionInfo info, diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 76f93e18dda4..a81c1b0bf9c3 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -26,6 +26,7 @@ import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.VectorDrawable; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.annotation.Nullable; @@ -145,4 +146,10 @@ public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAni mAnimator = null; } } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setTextEntryKey(true); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 871d57da9b0d..dcfa775dcabf 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -30,7 +30,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.TextView; import androidx.annotation.Nullable; @@ -105,8 +105,6 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { } setOnClickListener(mListener); - setOnHoverListener(new LiftToActivateListener( - (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE))); mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( @@ -240,4 +238,10 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { public void setAnimationEnabled(boolean enabled) { mAnimationsEnabled = enabled; } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setTextEntryKey(true); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt index bddf3b07dbb5..d2ad096c1207 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt @@ -97,6 +97,26 @@ constructor( ) } + fun logUpdateLockScreenUserLockedMsg( + userId: Int, + userUnlocked: Boolean, + encryptedOrLockdown: Boolean, + ) { + buffer.log( + KeyguardIndicationController.TAG, + LogLevel.DEBUG, + { + int1 = userId + bool1 = userUnlocked + bool2 = encryptedOrLockdown + }, + { + "updateLockScreenUserLockedMsg userId=$int1 " + + "userUnlocked:$bool1 encryptedOrLockdown:$bool2" + } + ) + } + fun logUpdateBatteryIndication( powerIndication: String, pluggedIn: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 43728260248a..0f5f869cba5d 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -20,12 +20,11 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.pm.UserInfo; -import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.UserHandle; import androidx.annotation.NonNull; +import com.android.systemui.res.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.GuestResetOrExitSessionReceiver.ResetSessionDialogFactory; @@ -33,7 +32,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.QSUserSwitcherEvent; -import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -63,7 +61,6 @@ public class GuestResumeSessionReceiver { private final SecureSettings mSecureSettings; private final ResetSessionDialogFactory mResetSessionDialogFactory; private final GuestSessionNotification mGuestSessionNotification; - private final HandlerThread mHandlerThread; @VisibleForTesting public final UserTracker.Callback mUserChangedCallback = @@ -114,16 +111,13 @@ public class GuestResumeSessionReceiver { mSecureSettings = secureSettings; mGuestSessionNotification = guestSessionNotification; mResetSessionDialogFactory = resetSessionDialogFactory; - mHandlerThread = new HandlerThread("GuestResumeSessionReceiver"); - mHandlerThread.start(); } /** * Register this receiver with the {@link BroadcastDispatcher} */ public void register() { - mUserTracker.addCallback(mUserChangedCallback, - new HandlerExecutor(mHandlerThread.getThreadHandler())); + mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); } private void cancelDialog() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java index 3ca95e11d789..5171a1f22791 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java @@ -19,6 +19,7 @@ package com.android.systemui.accessibility; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; @@ -28,10 +29,12 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.hardware.display.DisplayManager; +import android.os.Binder; import android.os.Handler; import android.util.SparseArray; import android.view.Display; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IMagnificationConnection; @@ -40,6 +43,7 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.CoreStartable; +import com.android.systemui.Flags; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.model.SysUiState; @@ -49,6 +53,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; +import java.util.function.Supplier; import javax.inject.Inject; @@ -101,19 +106,28 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { @Override protected WindowMagnificationController createInstance(Display display) { final Context windowContext = mContext.createWindowContext(display, - TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null); + Flags.createWindowlessWindowMagnifier() + ? TYPE_ACCESSIBILITY_OVERLAY + : TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, + /* options */ null); windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI); + + Supplier<SurfaceControlViewHost> scvhSupplier = () -> + Flags.createWindowlessWindowMagnifier() ? new SurfaceControlViewHost(mContext, + mContext.getDisplay(), new Binder(), TAG) : null; + return new WindowMagnificationController( windowContext, mHandler, new WindowMagnificationAnimationController(windowContext), - new SfVsyncFrameCallbackProvider(), - null, + /* mirrorWindowControl= */ null, new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState, - WindowManagerGlobal::getWindowSession, - mSecureSettings); + mSecureSettings, + scvhSupplier, + new SfVsyncFrameCallbackProvider(), + WindowManagerGlobal::getWindowSession); } } @@ -140,7 +154,7 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks { @Override protected MagnificationSettingsController createInstance(Display display) { final Context windowContext = mContext.createWindowContext(display, - TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null); + TYPE_ACCESSIBILITY_OVERLAY, /* options */ null); windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI); return new MagnificationSettingsController( windowContext, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index dde9f48424ea..d65cd5c09dcf 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -63,23 +63,27 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowMetrics; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.IRemoteMagnificationAnimationCallback; import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.annotation.UiThread; import androidx.core.math.MathUtils; import com.android.internal.accessibility.common.MagnificationConstants; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; +import com.android.systemui.Flags; import com.android.systemui.model.SysUiState; import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; @@ -158,6 +162,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private int mMagnificationFrameOffsetX = 0; private int mMagnificationFrameOffsetY = 0; + @Nullable private Supplier<SurfaceControlViewHost> mScvhSupplier; + + /** + * SurfaceControlViewHost is used to control the position of the window containing + * {@link #mMirrorView}. Using SurfaceControlViewHost instead of a regular window enables + * changing the window's position and setting {@link #mMirrorSurface}'s geometry atomically. + */ + private SurfaceControlViewHost mSurfaceControlViewHost; + // The root of the mirrored content private SurfaceControl mMirrorSurface; @@ -236,21 +249,21 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold @UiContext Context context, @NonNull Handler handler, @NonNull WindowMagnificationAnimationController animationController, - SfVsyncFrameCallbackProvider sfVsyncFrameProvider, MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, @NonNull WindowMagnifierCallback callback, SysUiState sysUiState, - @NonNull Supplier<IWindowSession> globalWindowSessionSupplier, - SecureSettings secureSettings) { + SecureSettings secureSettings, + Supplier<SurfaceControlViewHost> scvhSupplier, + SfVsyncFrameCallbackProvider sfVsyncFrameProvider, + Supplier<IWindowSession> globalWindowSessionSupplier) { mContext = context; mHandler = handler; mAnimationController = animationController; - mGlobalWindowSessionSupplier = globalWindowSessionSupplier; mAnimationController.setWindowMagnificationController(this); - mSfVsyncFrameProvider = sfVsyncFrameProvider; mWindowMagnifierCallback = callback; mSysUiState = sysUiState; + mScvhSupplier = scvhSupplier; mConfiguration = new Configuration(context.getResources().getConfiguration()); mWindowMagnificationSizePrefs = new WindowMagnificationSizePrefs(mContext); @@ -288,22 +301,78 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mTransaction = transaction; mGestureDetector = new MagnificationGestureDetector(mContext, handler, this); + mWindowInsetChangeRunnable = this::onWindowInsetChanged; + mGlobalWindowSessionSupplier = globalWindowSessionSupplier; + mSfVsyncFrameProvider = sfVsyncFrameProvider; // Initialize listeners. - mMirrorViewRunnable = () -> { - if (mMirrorView != null) { - final Rect oldViewBounds = new Rect(mMirrorViewBounds); - mMirrorView.getBoundsOnScreen(mMirrorViewBounds); - if (oldViewBounds.width() != mMirrorViewBounds.width() - || oldViewBounds.height() != mMirrorViewBounds.height()) { - mMirrorView.setSystemGestureExclusionRects(Collections.singletonList( - new Rect(0, 0, mMirrorViewBounds.width(), mMirrorViewBounds.height()))); + if (Flags.createWindowlessWindowMagnifier()) { + mMirrorViewRunnable = new Runnable() { + final Rect mPreviousBounds = new Rect(); + + @Override + public void run() { + if (mMirrorView != null) { + if (mPreviousBounds.width() != mMirrorViewBounds.width() + || mPreviousBounds.height() != mMirrorViewBounds.height()) { + mMirrorView.setSystemGestureExclusionRects(Collections.singletonList( + new Rect(0, 0, mMirrorViewBounds.width(), + mMirrorViewBounds.height()))); + mPreviousBounds.set(mMirrorViewBounds); + } + updateSystemUIStateIfNeeded(); + mWindowMagnifierCallback.onWindowMagnifierBoundsChanged( + mDisplayId, mMirrorViewBounds); + } } - updateSystemUIStateIfNeeded(); - mWindowMagnifierCallback.onWindowMagnifierBoundsChanged( - mDisplayId, mMirrorViewBounds); - } - }; + }; + + mMirrorSurfaceViewLayoutChangeListener = + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> + mMirrorView.post(this::applyTapExcludeRegion); + + mMirrorViewGeometryVsyncCallback = null; + } else { + mMirrorViewRunnable = () -> { + if (mMirrorView != null) { + final Rect oldViewBounds = new Rect(mMirrorViewBounds); + mMirrorView.getBoundsOnScreen(mMirrorViewBounds); + if (oldViewBounds.width() != mMirrorViewBounds.width() + || oldViewBounds.height() != mMirrorViewBounds.height()) { + mMirrorView.setSystemGestureExclusionRects(Collections.singletonList( + new Rect(0, 0, + mMirrorViewBounds.width(), mMirrorViewBounds.height()))); + } + updateSystemUIStateIfNeeded(); + mWindowMagnifierCallback.onWindowMagnifierBoundsChanged( + mDisplayId, mMirrorViewBounds); + } + }; + + mMirrorSurfaceViewLayoutChangeListener = + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> + mMirrorView.post(this::applyTapExcludeRegion); + + mMirrorViewGeometryVsyncCallback = + l -> { + if (isActivated() && mMirrorSurface != null && calculateSourceBounds( + mMagnificationFrame, mScale)) { + // The final destination for the magnification surface should be at 0,0 + // since the ViewRootImpl's position will change + mTmpRect.set(0, 0, mMagnificationFrame.width(), + mMagnificationFrame.height()); + mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect, + Surface.ROTATION_0).apply(); + + // Notify source bounds change when the magnifier is not animating. + if (!mAnimationController.isAnimating()) { + mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, + mSourceBounds); + } + } + }; + } + mMirrorViewLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { if (!mHandler.hasCallbacks(mMirrorViewRunnable)) { @@ -311,34 +380,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } }; - mMirrorSurfaceViewLayoutChangeListener = - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> - mMirrorView.post(this::applyTapExcludeRegion); - - mMirrorViewGeometryVsyncCallback = - l -> { - if (isActivated() && mMirrorSurface != null && calculateSourceBounds( - mMagnificationFrame, mScale)) { - // The final destination for the magnification surface should be at 0,0 - // since the ViewRootImpl's position will change - mTmpRect.set(0, 0, mMagnificationFrame.width(), - mMagnificationFrame.height()); - mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect, - Surface.ROTATION_0).apply(); - - // Notify source bounds change when the magnifier is not animating. - if (!mAnimationController.isAnimating()) { - mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, - mSourceBounds); - } - } - }; mUpdateStateDescriptionRunnable = () -> { if (isActivated()) { mMirrorView.setStateDescription(formatStateDescription(mScale)); } }; - mWindowInsetChangeRunnable = this::onWindowInsetChanged; } private void setupMagnificationSizeScaleOptions() { @@ -448,13 +494,21 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold if (mMirrorView != null) { mHandler.removeCallbacks(mMirrorViewRunnable); mMirrorView.removeOnLayoutChangeListener(mMirrorViewLayoutChangeListener); - mWm.removeView(mMirrorView); + if (!Flags.createWindowlessWindowMagnifier()) { + mWm.removeView(mMirrorView); + } mMirrorView = null; } if (mMirrorWindowControl != null) { mMirrorWindowControl.destroyControl(); } + + if (mSurfaceControlViewHost != null) { + mSurfaceControlViewHost.release(); + mSurfaceControlViewHost = null; + } + mMirrorViewBounds.setEmpty(); mSourceBounds.setEmpty(); updateSystemUIStateIfNeeded(); @@ -551,7 +605,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold if (!isActivated()) return; LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams(); params.accessibilityTitle = getAccessibilityWindowTitle(); - mWm.updateViewLayout(mMirrorView, params); + if (Flags.createWindowlessWindowMagnifier()) { + mSurfaceControlViewHost.relayout(params); + } else { + mWm.updateViewLayout(mMirrorView, params); + } } /** @@ -602,6 +660,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } private void createMirrorWindow() { + if (Flags.createWindowlessWindowMagnifier()) { + createWindowlessMirrorWindow(); + return; + } + // The window should be the size the mirrored surface will be but also add room for the // border and the drag handle. int windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin; @@ -652,6 +715,68 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold addDragTouchListeners(); } + private void createWindowlessMirrorWindow() { + // The window should be the size the mirrored surface will be but also add room for the + // border and the drag handle. + int windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin; + int windowHeight = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin; + + // TODO delete TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, it shouldn't be needed anymore + + LayoutParams params = new LayoutParams( + windowWidth, windowHeight, + LayoutParams.TYPE_ACCESSIBILITY_OVERLAY, + LayoutParams.FLAG_NOT_TOUCH_MODAL + | LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSPARENT); + params.receiveInsetsIgnoringZOrder = true; + params.setTitle(mContext.getString(R.string.magnification_window_title)); + params.accessibilityTitle = getAccessibilityWindowTitle(); + params.setTrustedOverlay(); + + mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null); + mMirrorSurfaceView = mMirrorView.findViewById(R.id.surface_view); + + mMirrorBorderView = mMirrorView.findViewById(R.id.magnification_inner_border); + + // Allow taps to go through to the mirror SurfaceView below. + mMirrorSurfaceView.addOnLayoutChangeListener(mMirrorSurfaceViewLayoutChangeListener); + + mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener); + mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate()); + mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> { + if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) { + mHandler.post(mWindowInsetChangeRunnable); + } + return v.onApplyWindowInsets(insets); + }); + + mSurfaceControlViewHost = mScvhSupplier.get(); + mSurfaceControlViewHost.setView(mMirrorView, params); + SurfaceControl surfaceControl = mSurfaceControlViewHost + .getSurfacePackage().getSurfaceControl(); + + int x = mMagnificationFrame.left - mMirrorSurfaceMargin; + int y = mMagnificationFrame.top - mMirrorSurfaceMargin; + mTransaction + .setCrop(surfaceControl, new Rect(0, 0, windowWidth, windowHeight)) + .setPosition(surfaceControl, x, y) + .setLayer(surfaceControl, Integer.MAX_VALUE) + .show(surfaceControl) + .apply(); + + mMirrorViewBounds.set(x, y, x + windowWidth, y + windowHeight); + + AccessibilityManager accessibilityManager = mContext + .getSystemService(AccessibilityManager.class); + accessibilityManager.attachAccessibilityOverlayToDisplay(mDisplayId, surfaceControl); + + SurfaceHolder holder = mMirrorSurfaceView.getHolder(); + holder.addCallback(this); + holder.setFormat(PixelFormat.RGBA_8888); + addDragTouchListeners(); + } + private void onWindowInsetChanged() { if (updateSystemGestureInsetsTop()) { updateSystemUIStateIfNeeded(); @@ -659,6 +784,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } private void applyTapExcludeRegion() { + if (Flags.createWindowlessWindowMagnifier()) { + applyTouchableRegion(); + return; + } + // Sometimes this can get posted and run after deleteWindowMagnification() is called. if (mMirrorView == null) return; @@ -709,6 +839,51 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return regionInsideDragBorder; } + private void applyTouchableRegion() { + // Sometimes this can get posted and run after deleteWindowMagnification() is called. + if (mMirrorView == null) return; + + var surfaceControl = mSurfaceControlViewHost.getRootSurfaceControl(); + surfaceControl.setTouchableRegion(calculateTouchableRegion()); + } + + private Region calculateTouchableRegion() { + Region touchableRegion = new Region(0, 0, mMirrorView.getWidth(), mMirrorView.getHeight()); + + Region regionInsideDragBorder = new Region(mBorderDragSize, mBorderDragSize, + mMirrorView.getWidth() - mBorderDragSize, + mMirrorView.getHeight() - mBorderDragSize); + touchableRegion.op(regionInsideDragBorder, Region.Op.DIFFERENCE); + + Rect dragArea = new Rect(); + mDragView.getHitRect(dragArea); + + Rect topLeftArea = new Rect(); + mTopLeftCornerView.getHitRect(topLeftArea); + + Rect topRightArea = new Rect(); + mTopRightCornerView.getHitRect(topRightArea); + + Rect bottomLeftArea = new Rect(); + mBottomLeftCornerView.getHitRect(bottomLeftArea); + + Rect bottomRightArea = new Rect(); + mBottomRightCornerView.getHitRect(bottomRightArea); + + Rect closeArea = new Rect(); + mCloseView.getHitRect(closeArea); + + // Add touchable regions for drag and close + touchableRegion.op(dragArea, Region.Op.UNION); + touchableRegion.op(topLeftArea, Region.Op.UNION); + touchableRegion.op(topRightArea, Region.Op.UNION); + touchableRegion.op(bottomLeftArea, Region.Op.UNION); + touchableRegion.op(bottomRightArea, Region.Op.UNION); + touchableRegion.op(closeArea, Region.Op.UNION); + + return touchableRegion; + } + private String getAccessibilityWindowTitle() { return mResources.getString(com.android.internal.R.string.android_system_label); } @@ -852,8 +1027,84 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * {@link #mMagnificationFrame}. */ private void modifyWindowMagnification(boolean computeWindowSize) { - mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback); - updateMirrorViewLayout(computeWindowSize); + if (Flags.createWindowlessWindowMagnifier()) { + updateMirrorSurfaceGeometry(); + updateWindowlessMirrorViewLayout(computeWindowSize); + } else { + mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback); + updateMirrorViewLayout(computeWindowSize); + } + } + + /** + * Updates {@link #mMirrorSurface}'s geometry. This modifies {@link #mTransaction} but does not + * apply it. + */ + @UiThread + private void updateMirrorSurfaceGeometry() { + if (isActivated() && mMirrorSurface != null + && calculateSourceBounds(mMagnificationFrame, mScale)) { + // The final destination for the magnification surface should be at 0,0 + // since the ViewRootImpl's position will change + mTmpRect.set(0, 0, mMagnificationFrame.width(), mMagnificationFrame.height()); + mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect, Surface.ROTATION_0); + + // Notify source bounds change when the magnifier is not animating. + if (!mAnimationController.isAnimating()) { + mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds); + } + } + } + + /** + * Updates the position of {@link mSurfaceControlViewHost} and layout params of MirrorView based + * on the position and size of {@link #mMagnificationFrame}. + * + * @param computeWindowSize set to {@code true} to compute window size with + * {@link #mMagnificationFrame}. + */ + @UiThread + private void updateWindowlessMirrorViewLayout(boolean computeWindowSize) { + if (!isActivated()) { + return; + } + + final int width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin; + final int height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin; + + final int minX = -mOuterBorderSize; + final int maxX = mWindowBounds.right - width + mOuterBorderSize; + final int x = MathUtils.clamp(mMagnificationFrame.left - mMirrorSurfaceMargin, minX, maxX); + + final int minY = -mOuterBorderSize; + final int maxY = mWindowBounds.bottom - height + mOuterBorderSize; + final int y = MathUtils.clamp(mMagnificationFrame.top - mMirrorSurfaceMargin, minY, maxY); + + if (computeWindowSize) { + LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams(); + params.width = width; + params.height = height; + mSurfaceControlViewHost.relayout(params); + mTransaction.setCrop(mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl(), + new Rect(0, 0, width, height)); + } + + mMirrorViewBounds.set(x, y, x + width, y + height); + mTransaction.setPosition( + mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl(), x, y); + if (computeWindowSize) { + mSurfaceControlViewHost.getRootSurfaceControl().applyTransactionOnDraw(mTransaction); + } else { + mTransaction.apply(); + } + + // If they are not dragging the handle, we can move the drag handle immediately without + // disruption. But if they are dragging it, we avoid moving until the end of the drag. + if (!mIsDragging) { + mMirrorView.post(this::maybeRepositionButton); + } + + mMirrorViewRunnable.run(); } /** @@ -1094,7 +1345,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold /** * Enables window magnification with specified parameters. If the given scale is <strong>less - * than or equal to 1.0f<strong>, then + * than or equal to 1.0f</strong>, then * {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to * be consistent with the behavior of display magnification. * @@ -1110,7 +1361,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold /** * Enables window magnification with specified parameters. If the given scale is <strong>less - * than 1.0f<strong>, then + * than 1.0f</strong>, then * {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to * be consistent with the behavior of display magnification. * @@ -1426,10 +1677,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mDragView.getLayoutParams(); - mMirrorView.getBoundsOnScreen(mTmpRect); - final int newGravity; - if (mTmpRect.right >= screenEdgeX) { + if (mMirrorViewBounds.right >= screenEdgeX) { newGravity = Gravity.BOTTOM | Gravity.LEFT; } else { newGravity = Gravity.BOTTOM | Gravity.RIGHT; @@ -1449,8 +1698,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mSettingsPanelVisibility = settingsPanelIsShown; mDragView.setBackground(mContext.getResources().getDrawable(settingsPanelIsShown - ? R.drawable.accessibility_window_magnification_drag_handle_background_change - : R.drawable.accessibility_window_magnification_drag_handle_background)); + ? R.drawable.accessibility_window_magnification_drag_handle_background_change_inset + : R.drawable.accessibility_window_magnification_drag_handle_background_inset)); PorterDuffColorFilter filter = new PorterDuffColorFilter( mContext.getColor(settingsPanelIsShown diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java index 0538e7d55bfd..4a06ae9523fa 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java @@ -21,7 +21,6 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRA import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY; import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE; import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets; import static com.android.systemui.accessibility.floatingmenu.MenuFadeEffectInfoKt.DEFAULT_FADE_EFFECT_IS_ENABLED; @@ -47,6 +46,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Prefs; @@ -182,7 +182,7 @@ class MenuInfoRepository { } void loadMenuTargetFeatures(OnInfoReady<List<AccessibilityTarget>> callback) { - callback.onReady(getTargets(mContext, ACCESSIBILITY_BUTTON)); + callback.onReady(getTargets(mContext, ShortcutConstants.UserShortcutType.SOFTWARE)); } void loadMenuSizeType(OnInfoReady<Integer> callback) { @@ -223,7 +223,7 @@ class MenuInfoRepository { private void onTargetFeaturesChanged() { mSettingsContentsCallback.onTargetFeaturesChanged( - getTargets(mContext, ACCESSIBILITY_BUTTON)); + getTargets(mContext, ShortcutConstants.UserShortcutType.SOFTWARE)); } private Position getStartPosition() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java index 334cc87144f9..035ccbd19f59 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java @@ -226,15 +226,15 @@ class MenuView extends FrameLayout implements // onArrivalAtPosition() is called at the end of the animation. } else { mMenuAnimationController.moveToPosition(position); - onArrivalAtPosition(); // no animation, so we call this immediately. + onArrivalAtPosition(true); // no animation, so we call this immediately. } } - void onArrivalAtPosition() { + void onArrivalAtPosition(boolean moveToEdgeIfTucked) { final PointF position = getMenuPosition(); onBoundsInParentChanged((int) position.x, (int) position.y); - if (isMoveToTucked()) { + if (isMoveToTucked() && moveToEdgeIfTucked) { mMenuAnimationController.moveToEdgeAndHide(); } } 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 bb5364d798da..f2e9531666e3 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -17,7 +17,6 @@ package com.android.systemui.accessibility.floatingmenu; import static android.view.WindowInsets.Type.ime; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static androidx.core.view.WindowInsetsCompat.Type; @@ -64,6 +63,7 @@ import androidx.lifecycle.Observer; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto; @@ -154,7 +154,7 @@ class MenuViewLayer extends FrameLayout implements final List<ComponentName> hardwareKeyShortcutComponents = mAccessibilityManager.getAccessibilityShortcutTargets( - ACCESSIBILITY_SHORTCUT_KEY) + ShortcutConstants.UserShortcutType.HARDWARE) .stream() .map(ComponentName::unflattenFromString) .toList(); @@ -446,7 +446,7 @@ class MenuViewLayer extends FrameLayout implements } } if (Flags.floatingMenuImeDisplacementAnimation()) { - mMenuView.onArrivalAtPosition(); + mMenuView.onArrivalAtPosition(false); } } 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 fdccad137a24..8ca083fa1e35 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 @@ -30,11 +30,16 @@ 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.domain.interactor.SelectedUserInteractor import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlin.math.max +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -44,6 +49,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** * Hosts application business logic related to user authentication. @@ -57,6 +63,7 @@ class AuthenticationInteractor @Inject constructor( @Application private val applicationScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, private val repository: AuthenticationRepository, private val selectedUserInteractor: SelectedUserInteractor, ) { @@ -227,6 +234,11 @@ constructor( if (authenticationResult.isSuccessful) { repository.reportAuthenticationAttempt(isSuccessful = true) _onAuthenticationResult.emit(true) + + // Force a garbage collection in an attempt to erase any credentials left in memory. + // Do it after a 5-sec delay to avoid making the bouncer dismiss animation janky. + initiateGarbageCollection(delay = 5.seconds) + return AuthenticationResult.SUCCEEDED } @@ -312,6 +324,15 @@ constructor( } } + private suspend fun initiateGarbageCollection(delay: Duration) { + applicationScope.launch(backgroundDispatcher) { + delay(delay) + System.gc() + System.runFinalization() + System.gc() + } + } + companion object { const val TAG = "AuthenticationInteractor" } 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 8e14778f1aa7..5c9c997db7e4 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 @@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Holds UI state and handles user input for the password bouncer UI. */ class PasswordBouncerViewModel( @@ -48,9 +49,6 @@ class PasswordBouncerViewModel( override val lockoutMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message - /** Whether the input method editor (for example, the software keyboard) is visible. */ - private var isImeVisible: Boolean = false - /** Whether the text field element currently has focus. */ private val isTextFieldFocused = MutableStateFlow(false) @@ -65,7 +63,6 @@ class PasswordBouncerViewModel( override fun onHidden() { super.onHidden() - isImeVisible = false isTextFieldFocused.value = false } @@ -97,16 +94,9 @@ class PasswordBouncerViewModel( } } - /** - * Notifies that the input method editor (for example, the software keyboard) has been shown or - * hidden. - */ - suspend fun onImeVisibilityChanged(isVisible: Boolean) { - if (isImeVisible && !isVisible && isInputEnabled.value) { - interactor.onImeHiddenByUser() - } - - isImeVisible = isVisible + /** Notifies that the user has dismissed the software keyboard (IME). */ + fun onImeDismissed() { + viewModelScope.launch { interactor.onImeHiddenByUser() } } /** Notifies that the password text field has gained or lost focus. */ diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt new file mode 100644 index 000000000000..f7ba5a44f4c9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal + +import com.android.systemui.CoreStartable +import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dock.DockManager +import com.android.systemui.dock.retrieveIsDocked +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.util.kotlin.sample +import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.onEach + +/** + * A [CoreStartable] responsible for automatically navigating between communal scenes when certain + * conditions are met. + */ +@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) +@SysUISingleton +class CommunalSceneStartable +@Inject +constructor( + private val dockManager: DockManager, + private val communalInteractor: CommunalInteractor, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, + @Application private val applicationScope: CoroutineScope, + @Background private val bgScope: CoroutineScope, +) : CoreStartable { + override fun start() { + // Handle automatically switching based on keyguard state. + keyguardTransitionInteractor.startedKeyguardTransitionStep + .mapLatest(::determineSceneAfterTransition) + .filterNotNull() + // TODO(b/322787129): Also set a custom transition animation here to avoid the regular + // slide-in animation when setting the scene programmatically + .onEach { nextScene -> communalInteractor.onSceneChanged(nextScene) } + .launchIn(applicationScope) + + // Handle automatically switching to communal when docked. + dockManager + .retrieveIsDocked() + // Allow some time after docking to ensure the dream doesn't start. If the dream + // starts, then we don't want to automatically transition to glanceable hub. + .debounce(DOCK_DEBOUNCE_DELAY) + .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair) + .onEach { (docked, lastStartedState) -> + if (docked && lastStartedState == KeyguardState.LOCKSCREEN) { + communalInteractor.onSceneChanged(CommunalSceneKey.Communal) + } + } + .launchIn(bgScope) + } + + private suspend fun determineSceneAfterTransition( + lastStartedTransition: TransitionStep, + ): CommunalSceneKey? { + val to = lastStartedTransition.to + val from = lastStartedTransition.from + val docked = dockManager.isDocked + + return when { + to == KeyguardState.DREAMING -> CommunalSceneKey.Blank + docked && to == KeyguardState.LOCKSCREEN && from != KeyguardState.GLANCEABLE_HUB -> { + CommunalSceneKey.Communal + } + to == KeyguardState.GONE -> CommunalSceneKey.Blank + !docked && !KeyguardState.deviceIsAwakeInState(to) -> { + // If the user taps the screen and wakes the device within this timeout, we don't + // want to dismiss the hub + delay(AWAKE_DEBOUNCE_DELAY) + CommunalSceneKey.Blank + } + else -> null + } + } + + companion object { + val AWAKE_DEBOUNCE_DELAY = 5.seconds + val DOCK_DEBOUNCE_DELAY = 1.seconds + } +} 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 28adb77f00e0..80fee640974d 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 @@ -33,7 +33,6 @@ import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.smartspace.data.repository.SmartspaceRepository import com.android.systemui.user.data.repository.UserRepository @@ -41,11 +40,9 @@ import com.android.systemui.util.kotlin.BooleanFlowOperators.and import com.android.systemui.util.kotlin.BooleanFlowOperators.not import com.android.systemui.util.kotlin.BooleanFlowOperators.or 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 import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine @@ -53,7 +50,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn /** Encapsulates business-logic related to communal mode. */ @OptIn(ExperimentalCoroutinesApi::class) @@ -61,7 +57,6 @@ import kotlinx.coroutines.flow.stateIn class CommunalInteractor @Inject constructor( - @Application applicationScope: CoroutineScope, private val communalRepository: CommunalRepository, private val widgetRepository: CommunalWidgetRepository, private val communalPrefsRepository: CommunalPrefsRepository, @@ -81,30 +76,15 @@ constructor( val isCommunalEnabled: Boolean get() = communalRepository.isCommunalEnabled - /** A {@link StateFlow} that tracks whether communal features are enabled. */ - val communalEnabledState: StateFlow<Boolean> - get() = communalRepository.communalEnabledState - /** Whether communal features are enabled and available. */ - val isCommunalAvailable: StateFlow<Boolean> = - flowOf(isCommunalEnabled) - .flatMapLatest { enabled -> - if (enabled) { - val isMainUser = userRepository.selectedUserInfo.map { it.isMain } - and( - isMainUser, - not(keyguardInteractor.isEncryptedOrLockdown), - or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming), - ) - } else { - flowOf(false) - } - } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, + val isCommunalAvailable: Flow<Boolean> = + and( + communalRepository.communalEnabledState, + userRepository.selectedUserInfo.map { it.isMain }, + not(keyguardInteractor.isEncryptedOrLockdown), + or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming) ) + .distinctUntilChanged() /** * Target scene as requested by the underlying [SceneTransitionLayout] or through @@ -176,8 +156,8 @@ constructor( } /** Show the widget editor Activity. */ - fun showWidgetEditor() { - editWidgetsActivityStarter.startActivity() + fun showWidgetEditor(preselectedKey: String? = null) { + editWidgetsActivityStarter.startActivity(preselectedKey) } /** Dismiss the CTA tile from the hub in view mode. */ 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 acd6cb09e241..ae019a187bae 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 @@ -128,4 +128,6 @@ sealed interface CommunalContentModel { } } } + + fun isWidget() = this is Widget } 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 1e64d3f9cedc..a87ebd7e8e9f 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 @@ -33,18 +33,16 @@ abstract class BaseCommunalViewModel( private val communalInteractor: CommunalInteractor, val mediaHost: MediaHost, ) { - val isCommunalAvailable: StateFlow<Boolean> = communalInteractor.isCommunalAvailable - val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene /** Whether widgets are currently being re-ordered. */ open val reorderingWidgets: StateFlow<Boolean> = MutableStateFlow(false) - private val _selectedIndex: MutableStateFlow<Int?> = MutableStateFlow(null) + private val _selectedKey: MutableStateFlow<String?> = MutableStateFlow(null) - /** The index of the currently selected item, or null if no item selected. */ - val selectedIndex: StateFlow<Int?> - get() = _selectedIndex + /** The key of the currently selected item, or null if no item selected. */ + val selectedKey: StateFlow<String?> + get() = _selectedKey fun onSceneChanged(scene: CommunalSceneKey) { communalInteractor.onSceneChanged(scene) @@ -94,8 +92,8 @@ abstract class BaseCommunalViewModel( */ open fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) {} - /** Called as the UI requests opening the widget editor. */ - open fun onOpenWidgetEditor() {} + /** Called as the UI requests opening the widget editor with an optional preselected widget. */ + open fun onOpenWidgetEditor(preselectedKey: String? = null) {} /** Called as the UI requests to dismiss the CTA tile. */ open fun onDismissCtaTile() {} @@ -109,8 +107,8 @@ abstract class BaseCommunalViewModel( /** Called as the user cancels dragging a widget to reorder. */ open fun onReorderWidgetCancel() {} - /** Set the index of the currently selected item */ - fun setSelectedIndex(index: Int?) { - _selectedIndex.value = index + /** Set the key of the currently selected item */ + fun setSelectedKey(key: String?) { + _selectedKey.value = key } } 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 4b98f1ae4fe8..ebcfb8bb8209 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 @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach /** The view model for communal hub in edit mode. */ @SysUISingleton @@ -44,10 +43,9 @@ constructor( // Only widgets are editable. The CTA tile comes last in the list and remains visible. override val communalContent: Flow<List<CommunalContentModel>> = - communalInteractor.widgetContent - // Clear the selected index when the list is updated. - .onEach { setSelectedIndex(null) } - .map { widgets -> widgets + listOf(CommunalContentModel.CtaTileInEditMode()) } + communalInteractor.widgetContent.map { widgets -> + widgets + listOf(CommunalContentModel.CtaTileInEditMode()) + } private val _reorderingWidgets = MutableStateFlow(false) @@ -61,7 +59,7 @@ constructor( override fun onReorderWidgetStart() { // Clear selection status - setSelectedIndex(null) + setSelectedKey(null) _reorderingWidgets.value = true uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index a909383f4a2c..d7a3705248ff 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -81,7 +81,8 @@ constructor( } } - override fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor() + override fun onOpenWidgetEditor(preselectedKey: String?) = + communalInteractor.showWidgetEditor(preselectedKey) override fun onDismissCtaTile() { scope.launch { 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 a2575439e4b2..54c709d4f053 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -29,6 +29,7 @@ import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.shared.log.CommunalUiEvent +import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent import javax.inject.Inject @@ -47,6 +48,7 @@ constructor( private const val EXTRA_FILTER_STRATEGY = "filter_strategy" private const val FILTER_STRATEGY_GLANCEABLE_HUB = 1 private const val TAG = "EditWidgetsActivity" + const val EXTRA_PRESELECTED_KEY = "preselected_key" } private val widgetConfigurator by lazy { widgetConfiguratorFactory.create(this) } @@ -92,6 +94,9 @@ constructor( windowInsetsController?.hide(WindowInsets.Type.systemBars()) window.setDecorFitsSystemWindows(false) + val preselectedKey = intent.getStringExtra(EXTRA_PRESELECTED_KEY) + communalViewModel.setSelectedKey(preselectedKey) + setCommunalEditWidgetActivityContent( activity = this, viewModel = communalViewModel, @@ -122,6 +127,7 @@ constructor( }, onEditDone = { try { + communalViewModel.onSceneChanged(CommunalSceneKey.Communal) checkNotNull(windowManagerService).lockNow(/* options */ null) finish() } catch (e: RemoteException) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt index 55acad0e9ed5..d1843afbc1c2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt @@ -18,12 +18,13 @@ package com.android.systemui.communal.widgets import android.content.Context import android.content.Intent +import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA_PRESELECTED_KEY import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.plugins.ActivityStarter import javax.inject.Inject interface EditWidgetsActivityStarter { - fun startActivity() + fun startActivity(preselectedKey: String? = null) } class EditWidgetsActivityStarterImpl @@ -33,10 +34,11 @@ constructor( private val activityStarter: ActivityStarter, ) : EditWidgetsActivityStarter { - override fun startActivity() { + override fun startActivity(preselectedKey: String?) { activityStarter.startActivityDismissingKeyguard( Intent(applicationContext, EditWidgetsActivity::class.java) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK), + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + .apply { preselectedKey?.let { putExtra(EXTRA_PRESELECTED_KEY, preselectedKey) } }, /* onlyProvisioned = */ true, /* dismissShade = */ true, ) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index 5c38264fbcf6..3072f74dff2b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -24,6 +24,7 @@ import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.plugins.PluginsModule; import com.android.systemui.unfold.UnfoldTransitionModule; import com.android.systemui.util.concurrency.GlobalConcurrencyModule; +import com.android.systemui.util.kotlin.GlobalCoroutinesModule; import dagger.Module; import dagger.Provides; @@ -47,6 +48,7 @@ import dagger.Provides; AndroidInternalsModule.class, FrameworkServicesModule.class, GlobalConcurrencyModule.class, + GlobalCoroutinesModule.class, UnfoldTransitionModule.class, PluginsModule.class, }) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 50f861fad9e4..e9d1e94a63b1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerIm import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import com.android.systemui.toast.ToastModule; +import com.android.systemui.unfold.UnfoldTransitionModule; import com.android.systemui.volume.dagger.VolumeModule; import com.android.systemui.wallpapers.dagger.WallpaperModule; @@ -107,6 +108,7 @@ import javax.inject.Named; ShadeModule.class, StartCentralSurfacesModule.class, SceneContainerFrameworkModule.class, + UnfoldTransitionModule.Startables.class, ToastModule.class, VolumeModule.class, WallpaperModule.class diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 95233f701bbb..9504cfcdbe3c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -24,6 +24,7 @@ import com.android.systemui.accessibility.Magnification import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.BiometricNotificationService import com.android.systemui.clipboardoverlay.ClipboardListener +import com.android.systemui.communal.CommunalSceneStartable import com.android.systemui.communal.log.CommunalLoggerStartable import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable import com.android.systemui.controls.dagger.StartControlsStartableModule @@ -50,7 +51,6 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.ImmersiveModeConfirmation import com.android.systemui.statusbar.gesture.GesturePointerEventListener import com.android.systemui.statusbar.notification.InstantAppNotifier -import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener import com.android.systemui.stylus.StylusUsiPowerStartable @@ -224,12 +224,6 @@ abstract class SystemUICoreStartableModule { @ClassKey(WMShell::class) abstract fun bindWMShell(sysui: WMShell): CoreStartable - /** Inject into KeyguardLiftController. */ - @Binds - @IntoMap - @ClassKey(KeyguardLiftController::class) - abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable - /** Inject into MediaTttSenderCoordinator. */ @Binds @IntoMap @@ -328,6 +322,11 @@ abstract class SystemUICoreStartableModule { @Binds @IntoMap + @ClassKey(CommunalSceneStartable::class) + abstract fun bindCommunalSceneStartable(impl: CommunalSceneStartable): CoreStartable + + @Binds + @IntoMap @ClassKey(CommunalAppWidgetHostStartable::class) abstract fun bindCommunalAppWidgetHostStartable( impl: CommunalAppWidgetHostStartable diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 92300efdc930..2587e2da2172 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -66,6 +66,7 @@ import com.android.systemui.log.dagger.MonitorLog; import com.android.systemui.log.table.TableLogBuffer; import com.android.systemui.mediaprojection.appselector.MediaProjectionModule; import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherModule; +import com.android.systemui.model.SceneContainerPlugin; import com.android.systemui.model.SysUiState; import com.android.systemui.motiontool.MotionToolModule; import com.android.systemui.navigationbar.NavigationBarComponent; @@ -133,7 +134,7 @@ import com.android.systemui.user.domain.UserDomainLayerModule; import com.android.systemui.util.EventLogModule; import com.android.systemui.util.concurrency.SysUIConcurrencyModule; import com.android.systemui.util.dagger.UtilModule; -import com.android.systemui.util.kotlin.CoroutinesModule; +import com.android.systemui.util.kotlin.SysUICoroutinesModule; import com.android.systemui.util.reference.ReferenceModule; import com.android.systemui.util.sensors.SensorModule; import com.android.systemui.util.settings.SettingsUtilModule; @@ -182,7 +183,6 @@ import javax.inject.Named; ConfigurationControllerModule.class, ConnectivityModule.class, ControlsModule.class, - CoroutinesModule.class, DemoModeModule.class, DeviceEntryModule.class, DisableFlagsModule.class, @@ -228,6 +228,7 @@ import javax.inject.Named; StatusBarWindowModule.class, SystemPropertiesFlagsModule.class, SysUIConcurrencyModule.class, + SysUICoroutinesModule.class, SysUIUnfoldModule.class, TelephonyRepositoryModule.class, TemporaryDisplayModule.class, @@ -268,8 +269,11 @@ public abstract class SystemUIModule { @SysUISingleton @Provides - static SysUiState provideSysUiState(DisplayTracker displayTracker, DumpManager dumpManager) { - final SysUiState state = new SysUiState(displayTracker); + static SysUiState provideSysUiState( + DisplayTracker displayTracker, + DumpManager dumpManager, + SceneContainerPlugin sceneContainerPlugin) { + final SysUiState state = new SysUiState(displayTracker, sceneContainerPlugin); dumpManager.registerDumpable(state); return state; } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt new file mode 100644 index 000000000000..1fd7d009cee4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.ui.binder + +import android.content.pm.PackageManager +import android.hardware.Sensor +import android.hardware.TriggerEvent +import android.hardware.TriggerEventListener +import com.android.keyguard.ActiveUnlockConfig +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.CoreStartable +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +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.domain.interactor.DeviceEntryFaceAuthInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.util.Assert +import com.android.systemui.util.sensors.AsyncSensorManager +import java.io.PrintWriter +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch + +/** + * Triggers face auth and active unlock on lift when the device is showing the lock screen or + * bouncer. Only initialized if face auth is supported on the device. Not to be confused with the + * lift to wake gesture which is handled by {@link com.android.server.policy.PhoneWindowManager}. + */ +@SysUISingleton +class LiftToRunFaceAuthBinder +@Inject +constructor( + @Application private val scope: CoroutineScope, + private val packageManager: PackageManager, + private val asyncSensorManager: AsyncSensorManager, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + keyguardInteractor: KeyguardInteractor, + primaryBouncerInteractor: PrimaryBouncerInteractor, + alternateBouncerInteractor: AlternateBouncerInteractor, + private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, + powerInteractor: PowerInteractor, +) : CoreStartable { + + private var pickupSensor: Sensor? = null + private val isListening: MutableStateFlow<Boolean> = MutableStateFlow(false) + private val stoppedListening: Flow<Unit> = isListening.filterNot { it }.map {} // map to Unit + + private val onAwakeKeyguard: Flow<Boolean> = + combine( + powerInteractor.isInteractive, + keyguardInteractor.isKeyguardVisible, + ) { isInteractive, isKeyguardVisible -> + isInteractive && isKeyguardVisible + } + private val bouncerShowing: Flow<Boolean> = + combine( + primaryBouncerInteractor.isShowing, + alternateBouncerInteractor.isVisible, + ) { primaryBouncerShowing, alternateBouncerShowing -> + primaryBouncerShowing || alternateBouncerShowing + } + private val listenForPickupSensor: Flow<Boolean> = + combine( + stoppedListening, + bouncerShowing, + onAwakeKeyguard, + ) { _, bouncerShowing, onAwakeKeyguard -> + (onAwakeKeyguard || bouncerShowing) && + deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() + } + + override fun start() { + if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { + init() + } + } + + private fun init() { + pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) + scope.launch { + listenForPickupSensor.collect { listenForPickupSensor -> + updateListeningState(listenForPickupSensor) + } + } + } + + private val listener: TriggerEventListener = + object : TriggerEventListener() { + override fun onTrigger(event: TriggerEvent?) { + Assert.isMainThread() + deviceEntryFaceAuthInteractor.onDeviceLifted() + keyguardUpdateMonitor.requestActiveUnlock( + ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, + "KeyguardLiftController" + ) + + // Not listening anymore since trigger events unregister themselves + isListening.value = false + } + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + pw.println("LiftToRunFaceAuthBinder:") + pw.println(" pickupSensor: $pickupSensor") + pw.println(" isListening: ${isListening.value}") + } + + private fun updateListeningState(shouldListen: Boolean) { + if (pickupSensor == null) { + return + } + if (shouldListen != isListening.value) { + isListening.value = shouldListen + + if (shouldListen) { + asyncSensorManager.requestTriggerSensor(listener, pickupSensor) + } else { + asyncSensorManager.cancelTriggerSensor(listener, pickupSensor) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialogDelegate.java index 5deea9bc737a..98642d75f907 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java +++ b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialogDelegate.java @@ -16,17 +16,25 @@ package com.android.systemui.keyboard; -import android.content.Context; import android.view.WindowManager; import com.android.systemui.statusbar.phone.SystemUIDialog; -public class BluetoothDialog extends SystemUIDialog { +import javax.inject.Inject; - public BluetoothDialog(Context context) { - super(context); +public class BluetoothDialogDelegate implements SystemUIDialog.Delegate{ - getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); - setShowForAllUsers(true); + private final SystemUIDialog.Factory mSystemUIDialogFactory; + @Inject + public BluetoothDialogDelegate(SystemUIDialog.Factory systemUIDialogFactory) { + mSystemUIDialogFactory = systemUIDialogFactory; + } + + @Override + public SystemUIDialog createDialog() { + SystemUIDialog dialog = mSystemUIDialogFactory.create(this); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + dialog.setShowForAllUsers(true); + return dialog; } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java index 1cdbe6fed58a..17e3ca6f1a8c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java @@ -52,6 +52,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.systemui.CoreStartable; import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; @@ -109,6 +110,7 @@ public class KeyboardUI implements CoreStartable, InputManager.OnTabletModeChang private final Provider<LocalBluetoothManager> mBluetoothManagerProvider; private final SecureSettings mSecureSettings; + private final BluetoothDialogDelegate mBluetoothDialogDelegate; private boolean mEnabled; private String mKeyboardName; @@ -121,16 +123,20 @@ public class KeyboardUI implements CoreStartable, InputManager.OnTabletModeChang private int mInTabletMode = InputManager.SWITCH_STATE_UNKNOWN; private int mScanAttempt = 0; private ScanCallback mScanCallback; - private BluetoothDialog mDialog; + private SystemUIDialog mDialog; private int mState; @Inject - public KeyboardUI(Context context, Provider<LocalBluetoothManager> bluetoothManagerProvider, - SecureSettings secureSettings) { + public KeyboardUI( + Context context, + Provider<LocalBluetoothManager> bluetoothManagerProvider, + SecureSettings secureSettings, + BluetoothDialogDelegate bluetoothDialogDelegate) { mContext = context; this.mBluetoothManagerProvider = bluetoothManagerProvider; mSecureSettings = secureSettings; + mBluetoothDialogDelegate = bluetoothDialogDelegate; } @Override @@ -437,7 +443,7 @@ public class KeyboardUI implements CoreStartable, InputManager.OnTabletModeChang new BluetoothDialogClickListener(); DialogInterface.OnDismissListener dismissListener = new BluetoothDialogDismissListener(); - mDialog = new BluetoothDialog(mContext); + mDialog = mBluetoothDialogDelegate.createDialog(); mDialog.setTitle(R.string.enable_bluetooth_title); mDialog.setMessage(R.string.enable_bluetooth_message); mDialog.setPositiveButton( diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt index 4ed812010100..5ef5ef19c809 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt @@ -43,4 +43,13 @@ class StickyKeysLogger @Inject constructor(@KeyboardLog private val buffer: LogB { "new sticky keys state received: $str1" } ) } -}
\ No newline at end of file + + fun logNewSettingValue(enabled: Boolean) { + buffer.log( + TAG, + LogLevel.INFO, + { bool1 = enabled }, + { "sticky key setting changed, new state: ${if (bool1) "enabled" else "disabled"}" } + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt index 34d288815570..ec29bd6014ef 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt @@ -19,8 +19,10 @@ package com.android.systemui.keyboard.stickykeys.data.repository import android.hardware.input.InputManager import android.hardware.input.InputManager.StickyModifierStateListener import android.hardware.input.StickyModifierState +import android.provider.Settings import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyboard.stickykeys.StickyKeysLogger import com.android.systemui.keyboard.stickykeys.shared.model.Locked @@ -30,14 +32,19 @@ import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT_GR import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT +import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import javax.inject.Inject interface StickyKeysRepository { @@ -45,11 +52,15 @@ interface StickyKeysRepository { val settingEnabled: Flow<Boolean> } +@SysUISingleton +@OptIn(ExperimentalCoroutinesApi::class) class StickyKeysRepositoryImpl @Inject constructor( private val inputManager: InputManager, @Background private val backgroundDispatcher: CoroutineDispatcher, + private val secureSettings: SecureSettings, + userRepository: UserRepository, private val stickyKeysLogger: StickyKeysLogger, ) : StickyKeysRepository { @@ -66,8 +77,26 @@ constructor( .onEach { stickyKeysLogger.logNewStickyKeysReceived(it) } .flowOn(backgroundDispatcher) - // TODO(b/319837892): Implement reading actual setting - override val settingEnabled: StateFlow<Boolean> = MutableStateFlow(true) + override val settingEnabled: Flow<Boolean> = + userRepository.selectedUserInfo + .flatMapLatest { stickyKeySettingObserver(it.id) } + .flowOn(backgroundDispatcher) + + private fun stickyKeySettingObserver(userId: Int): Flow<Boolean> { + return secureSettings + .observerFlow(userId, SETTING_KEY) + .onStart { emit(Unit) } + .map { isSettingEnabledForCurrentUser(userId) } + .distinctUntilChanged() + .onEach { stickyKeysLogger.logNewSettingValue(it) } + } + + private fun isSettingEnabledForCurrentUser(userId: Int) = + secureSettings.getIntForUser( + /* name= */ SETTING_KEY, + /* default= */ 0, + /* userHandle= */ userId + ) != 0 private fun toStickyKeysMap(state: StickyModifierState): LinkedHashMap<ModifierKey, Locked> { val keys = linkedMapOf<ModifierKey, Locked>() @@ -88,5 +117,6 @@ constructor( companion object { const val TAG = "StickyKeysRepositoryImpl" + const val SETTING_KEY = Settings.Secure.ACCESSIBILITY_STICKY_KEYS } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 8e3b19609142..794befa3725d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2184,6 +2184,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, */ public void showDismissibleKeyguard() { if (mFoldGracePeriodProvider.isEnabled()) { + if (!mUpdateMonitor.isDeviceProvisioned()) { + Log.d(TAG, "Device not provisioned, so ignore request to show keyguard."); + return; + } Bundle showKeyguardUnlocked = new Bundle(); showKeyguardUnlocked.putBoolean(OPTION_SHOW_DISMISSIBLE, true); showKeyguard(showKeyguardUnlocked); 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 13e38358477a..e16f8dcbb00e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -51,7 +51,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager; import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule; -import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule; +import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; @@ -104,7 +104,7 @@ import kotlinx.coroutines.CoroutineDispatcher; FalsingModule.class, KeyguardDataQuickAffordanceModule.class, KeyguardRepositoryModule.class, - KeyguardFaceAuthModule.class, + DeviceEntryFaceAuthModule.class, KeyguardDisplayModule.class, StartKeyguardTransitionModule.class, ResourceTrimmerModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthModule.kt index fede47957a7b..4cd544ff658e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthModule.kt @@ -23,6 +23,7 @@ import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepos import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor +import com.android.systemui.deviceentry.ui.binder.LiftToRunFaceAuthBinder import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.TableLogBufferFactory import dagger.Binds @@ -32,7 +33,7 @@ import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @Module -interface KeyguardFaceAuthModule { +interface DeviceEntryFaceAuthModule { @Binds fun deviceEntryFaceAuthRepository( impl: DeviceEntryFaceAuthRepositoryImpl @@ -41,13 +42,20 @@ interface KeyguardFaceAuthModule { @Binds @IntoMap @ClassKey(SystemUIDeviceEntryFaceAuthInteractor::class) - fun bind(impl: SystemUIDeviceEntryFaceAuthInteractor): CoreStartable + fun bindSystemUIDeviceEntryFaceAuthInteractor( + impl: SystemUIDeviceEntryFaceAuthInteractor + ): CoreStartable @Binds fun keyguardFaceAuthInteractor( impl: SystemUIDeviceEntryFaceAuthInteractor ): DeviceEntryFaceAuthInteractor + @Binds + @IntoMap + @ClassKey(LiftToRunFaceAuthBinder::class) + fun bindLiftToRunFaceAuthBinder(impl: LiftToRunFaceAuthBinder): CoreStartable + companion object { @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 1c6056c3b9d0..64e28700aa74 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -567,17 +567,17 @@ constructor( val callback = object : KeyguardUpdateMonitorCallback() { override fun onStrongAuthStateChanged(userId: Int) { - trySend(userId) + trySendWithFailureLogging(userId, TAG, "strong auth state change") } } - keyguardUpdateMonitor.registerCallback(callback) - awaitClose { keyguardUpdateMonitor.removeCallback(callback) } } .filter { userId -> userId == userTracker.userId } .onStart { emit(userTracker.userId) } .mapLatest { userId -> keyguardUpdateMonitor.isEncryptedOrLockdown(userId) } + // KeyguardUpdateMonitor#registerCallback needs to be called on the main thread. + .flowOn(mainDispatcher) override fun isKeyguardShowing(): Boolean { return keyguardStateController.isShowing 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 388834597f60..a1f94250e149 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 @@ -56,6 +56,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -102,10 +103,10 @@ constructor( quickAffordanceAlwaysVisible(position), keyguardInteractor.isDozing, keyguardInteractor.isKeyguardShowing, - shadeInteractor.anyExpansion, + shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(), biometricSettingsRepository.isCurrentUserInLockdown, - ) { affordance, isDozing, isKeyguardShowing, qsExpansion, isUserInLockdown -> - if (!isDozing && isKeyguardShowing && (qsExpansion < 1.0f) && !isUserInLockdown) { + ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown -> + if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) { affordance } else { KeyguardQuickAffordanceModel.Hidden 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 703bb879533c..873cc847fa60 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.binder import android.annotation.SuppressLint import android.content.res.ColorStateList +import android.util.StateSet import android.view.HapticFeedbackConstants import android.view.View import androidx.lifecycle.Lifecycle @@ -113,6 +114,8 @@ object DeviceEntryIconViewBinder { fgIconView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { + // Start with an empty state + fgIconView.setImageState(StateSet.NOTHING, /* merge */ false) launch { fgViewModel.viewModel.collect { viewModel -> fgIconView.setImageState( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt deleted file mode 100644 index 2feaa2e81c0f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt +++ /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 com.android.systemui.keyguard.ui.binder - -import android.os.Trace -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.constraintlayout.widget.ConstraintSet -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel -import com.android.systemui.lifecycle.repeatWhenAttached -import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch - -/** - * Binds the existing blueprint to the constraint layout that previews keyguard. - * - * This view binder should only inflate and add relevant views and apply the constraints. Actual - * data binding should be done in {@link KeyguardPreviewRenderer} - */ -class PreviewKeyguardBlueprintViewBinder { - companion object { - - /** - * Binds the existing blueprint to the constraint layout that previews keyguard. - * - * @param constraintLayout The root view to bind to - * @param viewModel The instance of the view model that contains flows we collect on. - * @param finishedAddViewCallback Called when we have finished inflating the views. - */ - fun bind( - constraintLayout: ConstraintLayout, - viewModel: KeyguardBlueprintViewModel, - finishedAddViewCallback: () -> Unit - ): DisposableHandle { - return constraintLayout.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.CREATED) { - launch { - viewModel.blueprint.collect { blueprint -> - val prevBluePrint = viewModel.currentBluePrint - Trace.beginSection("PreviewKeyguardBlueprint#applyBlueprint") - - ConstraintSet().apply { - clone(constraintLayout) - val emptyLayout = ConstraintSet.Layout() - knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) } - blueprint.applyConstraints(this) - // Add and remove views of sections that are not contained by the - // other. - blueprint.replaceViews( - prevBluePrint, - constraintLayout, - bindData = false - ) - applyTo(constraintLayout) - } - - viewModel.currentBluePrint = blueprint - finishedAddViewCallback.invoke() - Trace.endSection() - } - } - } - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index 841bad4c15cc..a0c0095b34a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -41,6 +41,7 @@ import android.view.WindowManager import android.widget.FrameLayout import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isInvisible import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch @@ -54,15 +55,12 @@ import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewM 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.FeatureFlagsClassic -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder -import com.android.systemui.keyguard.ui.binder.PreviewKeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.view.KeyguardRootView -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel @@ -124,19 +122,18 @@ constructor( private val broadcastDispatcher: BroadcastDispatcher, private val lockscreenSmartspaceController: LockscreenSmartspaceController, private val udfpsOverlayInteractor: UdfpsOverlayInteractor, - private val featureFlags: FeatureFlagsClassic, private val falsingManager: FalsingManager, private val vibratorHelper: VibratorHelper, private val indicationController: KeyguardIndicationController, private val keyguardRootViewModel: KeyguardRootViewModel, @Assisted bundle: Bundle, - private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel, private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel, private val chipbarCoordinator: ChipbarCoordinator, private val screenOffAnimationController: ScreenOffAnimationController, private val shadeInteractor: ShadeInteractor, private val secureSettings: SecureSettings, private val communalTutorialViewModel: CommunalTutorialIndicatorViewModel, + private val defaultShortcutsSection: DefaultShortcutsSection, ) { val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN) private val width: Int = bundle.getInt(KEY_VIEW_WIDTH) @@ -393,32 +390,32 @@ constructor( setUpUdfps(previewContext, rootView) - disposables.add( - PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) { - if (keyguardBottomAreaRefactor()) { - setupShortcuts(keyguardRootView) - } - - if (!shouldHideClock) { - setUpClock(previewContext, rootView) - KeyguardPreviewClockViewBinder.bind( - largeClockHostView, - smallClockHostView, - clockViewModel, - ) - } + if (keyguardBottomAreaRefactor()) { + setupShortcuts(keyguardRootView) + } - setUpSmartspace(previewContext, rootView) - smartSpaceView?.let { - KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel) - } + if (!shouldHideClock) { + setUpClock(previewContext, rootView) + KeyguardPreviewClockViewBinder.bind( + largeClockHostView, + smallClockHostView, + clockViewModel, + ) + } - setupCommunalTutorialIndicator(keyguardRootView) - } - ) + setUpSmartspace(previewContext, rootView) + smartSpaceView?.let { KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel) } + setupCommunalTutorialIndicator(keyguardRootView) } private fun setupShortcuts(keyguardRootView: ConstraintLayout) { + // Add shortcuts + val cs = ConstraintSet() + cs.clone(keyguardRootView) + defaultShortcutsSection.addViews(keyguardRootView) + defaultShortcutsSection.applyConstraints(cs) + cs.applyTo(keyguardRootView) + keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { imageView -> shortcutsBindings.add( KeyguardQuickAffordanceViewBinder.bind( @@ -476,53 +473,40 @@ constructor( } private fun setUpClock(previewContext: Context, parentView: ViewGroup) { - largeClockHostView = - if (KeyguardShadeMigrationNssl.isEnabled) { - parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view_large) - } else { - val hostView = FrameLayout(previewContext) - hostView.layoutParams = - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT, - ) - parentView.addView(hostView) - hostView - } + val resources = parentView.resources + largeClockHostView = FrameLayout(previewContext) + largeClockHostView.layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + parentView.addView(largeClockHostView) largeClockHostView.isInvisible = true - smallClockHostView = - if (KeyguardShadeMigrationNssl.isEnabled) { - parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view) - } else { - val resources = parentView.resources - val hostView = FrameLayout(previewContext) - val layoutParams = - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_height - ) - ) - layoutParams.topMargin = - KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) + - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_padding_top - ) - hostView.layoutParams = layoutParams - - hostView.setPaddingRelative( - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.clock_padding_start - ), - 0, - 0, - 0 + smallClockHostView = FrameLayout(previewContext) + val layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.small_clock_height ) - hostView.clipChildren = false - parentView.addView(hostView) - hostView - } + ) + layoutParams.topMargin = + KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) + + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.small_clock_padding_top + ) + smallClockHostView.layoutParams = layoutParams + smallClockHostView.setPaddingRelative( + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.clock_padding_start + ), + 0, + 0, + 0 + ) + smallClockHostView.clipChildren = false + parentView.addView(smallClockHostView) smallClockHostView.isInvisible = true // TODO (b/283465254): Move the listeners to KeyguardClockRepository diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt index 400d0dc2b242..a651c10d1c35 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt @@ -87,7 +87,8 @@ constructor( return } // This moves the existing NSSL view to a different parent, as the controller is a - // singleton and recreating it has other bad side effects + // singleton and recreating it has other bad side effects. + // In the SceneContainer, this is done by the NotificationSection composable. notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let { (it.parent as ViewGroup).removeView(it) sharedNotificationContainer.addNotificationStackScrollLayout(it) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt index a3029b284934..23ee00d88fdc 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt @@ -146,7 +146,7 @@ constructor( null, UserHandle.ALL ) - userTracker.addCallback(userTrackerCallback, backgroundExecutor) + userTracker.addCallback(userTrackerCallback, mainExecutor) loadSavedComponents() } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt index 11d0be5fc8bf..a618490c1b53 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt @@ -16,7 +16,7 @@ package com.android.systemui.mediaprojection -import android.os.IBinder +import android.app.ActivityOptions.LaunchCookie import android.os.Parcel import android.os.Parcelable @@ -24,12 +24,12 @@ import android.os.Parcelable * Class that represents an area that should be captured. Currently it has only a launch cookie that * represents a task but we potentially could add more identifiers e.g. for a pair of tasks. */ -data class MediaProjectionCaptureTarget(val launchCookie: IBinder?) : Parcelable { +data class MediaProjectionCaptureTarget(val launchCookie: LaunchCookie?) : Parcelable { - constructor(parcel: Parcel) : this(parcel.readStrongBinder()) + constructor(parcel: Parcel) : this(LaunchCookie.readFromParcel(parcel)) override fun writeToParcel(dest: Parcel, flags: Int) { - dest.writeStrongBinder(launchCookie) + LaunchCookie.writeToParcel(launchCookie, dest) } override fun describeContents(): Int = 0 diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt index 50e9e7517a0f..4685c5a0cb21 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt @@ -16,6 +16,7 @@ package com.android.systemui.mediaprojection.appselector import android.app.ActivityOptions +import android.app.ActivityOptions.LaunchCookie import android.content.Intent import android.content.res.Configuration import android.content.res.Resources @@ -24,9 +25,7 @@ import android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTE import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION import android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL import android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK -import android.os.Binder import android.os.Bundle -import android.os.IBinder import android.os.ResultReceiver import android.os.UserHandle import android.util.Log @@ -163,9 +162,9 @@ class MediaProjectionAppSelectorActivity( val intent = createIntent(targetInfo) - val launchToken: IBinder = Binder("media_projection_launch_token") + val launchCookie = LaunchCookie("media_projection_launch_token") val activityOptions = ActivityOptions.makeBasic() - activityOptions.launchCookie = launchToken + activityOptions.setLaunchCookie(launchCookie) val userHandle = mMultiProfilePagerAdapter.activeListAdapter.userHandle @@ -175,7 +174,7 @@ class MediaProjectionAppSelectorActivity( // is created and ready to be captured. val activityStarted = activityLauncher.startActivityAsUser(intent, userHandle, activityOptions.toBundle()) { - returnSelectedApp(launchToken) + returnSelectedApp(launchCookie) } // Rely on the ActivityManager to pop up a dialog regarding app suspension @@ -233,7 +232,7 @@ class MediaProjectionAppSelectorActivity( } } - override fun returnSelectedApp(launchCookie: IBinder) { + override fun returnSelectedApp(launchCookie: LaunchCookie) { taskSelected = true if (intent.hasExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER)) { // The client requested to return the result in the result receiver instead of @@ -255,7 +254,7 @@ class MediaProjectionAppSelectorActivity( val mediaProjectionBinder = intent.getIBinderExtra(EXTRA_MEDIA_PROJECTION) val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder) - projection.launchCookie = launchCookie + projection.setLaunchCookie(launchCookie) val intent = Intent() intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder()) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt index 93c3bce87ad3..f204b3e74f4b 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt @@ -1,6 +1,6 @@ package com.android.systemui.mediaprojection.appselector -import android.os.IBinder +import android.app.ActivityOptions.LaunchCookie /** * Interface that allows to continue the media projection flow and return the selected app @@ -11,5 +11,5 @@ interface MediaProjectionAppSelectorResultHandler { * Return selected app to the original caller of the media projection app picker. * @param launchCookie launch cookie of the launched activity of the target app */ - fun returnSelectedApp(launchCookie: IBinder) + fun returnSelectedApp(launchCookie: LaunchCookie) } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt index ba837dba5354..a811065fdc65 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt @@ -17,10 +17,10 @@ package com.android.systemui.mediaprojection.appselector.view import android.app.ActivityOptions +import android.app.ActivityOptions.LaunchCookie import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED import android.app.IActivityTaskManager import android.graphics.Rect -import android.os.Binder import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -121,7 +121,7 @@ constructor( } override fun onRecentAppClicked(task: RecentTask, view: View) { - val launchCookie = Binder() + val launchCookie = LaunchCookie() val activityOptions = ActivityOptions.makeScaleUpAnimation( view, @@ -132,7 +132,7 @@ constructor( ) activityOptions.pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED - activityOptions.launchCookie = launchCookie + activityOptions.setLaunchCookie(launchCookie) activityOptions.launchDisplayId = task.displayId activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle()) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index 039372d87835..8b034b293dcb 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -28,6 +28,7 @@ import static com.android.systemui.mediaprojection.permission.ScreenShareOptionK import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityOptions.LaunchCookie; import android.app.AlertDialog; import android.app.StatusBarManager; import android.content.Context; @@ -146,6 +147,13 @@ public class MediaProjectionPermissionActivity extends Activity final IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName, mReviewGrantedConsentRequired); + + LaunchCookie launchCookie = launchingIntent.getParcelableExtra( + MediaProjectionManager.EXTRA_LAUNCH_COOKIE, LaunchCookie.class); + if (launchCookie != null) { + projection.setLaunchCookie(launchCookie); + } + // Automatically grant consent if a system-privileged component is recording. final Intent intent = new Intent(); intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt new file mode 100644 index 000000000000..6eb62263eb9a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.model + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.ObservableTransitionState +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING +import dagger.Lazy +import javax.inject.Inject + +/** + * A plugin for [SysUiState] that provides overrides for certain state flags that must be pulled + * from the scene framework when that framework is enabled. + */ +@SysUISingleton +class SceneContainerPlugin +@Inject +constructor( + private val interactor: Lazy<SceneInteractor>, +) { + /** + * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled + * or if the flag value doesn't need to be overridden. + */ + fun flagValueOverride(flag: Int): Boolean? { + if (!SceneContainerFlag.isEnabled) { + return null + } + + val transitionState = interactor.get().transitionState.value + val idleTransitionStateOrNull = transitionState as? ObservableTransitionState.Idle + val currentSceneOrNull = idleTransitionStateOrNull?.scene + return currentSceneOrNull?.let { sceneKey -> EvaluatorByFlag[flag]?.invoke(sceneKey) } + } + + companion object { + + /** + * Value evaluator function by state flag ID. + * + * The value evaluator function can be invoked, passing in the current [SceneKey] to know + * the override value of the flag ID. + * + * If the map doesn't contain an entry for a certain flag ID, it means that it doesn't need + * to be overridden by the scene framework. + */ + val EvaluatorByFlag = + mapOf<Int, (SceneKey) -> Boolean>( + SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it != SceneKey.Gone }, + SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it == SceneKey.Shade }, + SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it == SceneKey.QuickSettings }, + SYSUI_STATE_BOUNCER_SHOWING to { it == SceneKey.Bouncer }, + SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it == SceneKey.Lockscreen }, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java index 3cdcb2c4f550..2dd2327e5387 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java @@ -41,13 +41,15 @@ public class SysUiState implements Dumpable { public static final boolean DEBUG = false; private final DisplayTracker mDisplayTracker; + private final SceneContainerPlugin mSceneContainerPlugin; private @QuickStepContract.SystemUiStateFlags int mFlags; private final List<SysUiStateCallback> mCallbacks = new ArrayList<>(); private int mFlagsToSet = 0; private int mFlagsToClear = 0; - public SysUiState(DisplayTracker displayTracker) { + public SysUiState(DisplayTracker displayTracker, SceneContainerPlugin sceneContainerPlugin) { mDisplayTracker = displayTracker; + mSceneContainerPlugin = sceneContainerPlugin; } /** @@ -71,6 +73,16 @@ public class SysUiState implements Dumpable { /** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */ public SysUiState setFlag(int flag, boolean enabled) { + final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag); + if (overrideOrNull != null && enabled != overrideOrNull) { + if (DEBUG) { + Log.d(TAG, "setFlag for flag " + flag + " and value " + enabled + " overridden to " + + overrideOrNull + " by scene container plugin"); + } + + enabled = overrideOrNull; + } + if (enabled) { mFlagsToSet |= flag; } else { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java index d7e062fb926f..7d13397b374c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java @@ -41,6 +41,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; @@ -55,12 +56,14 @@ import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.systemui.Dumpable; import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; import com.android.systemui.accessibility.SystemActions; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.recents.OverviewProxyService; @@ -79,6 +82,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -101,6 +105,7 @@ public final class NavBarHelper implements private static final String TAG = NavBarHelper.class.getSimpleName(); private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Executor mMainExecutor; private final AccessibilityManager mAccessibilityManager; private final Lazy<AssistManager> mAssistManagerLazy; private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy; @@ -185,7 +190,12 @@ public final class NavBarHelper implements DisplayTracker displayTracker, NotificationShadeWindowController notificationShadeWindowController, DumpManager dumpManager, - CommandQueue commandQueue) { + CommandQueue commandQueue, + @Main Executor mainExecutor) { + // b/319489709: This component shouldn't be running for a non-primary user + if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) { + Log.wtf(TAG, "Unexpected initialization for non-primary user", new Throwable()); + } mContext = context; mNotificationShadeWindowController = notificationShadeWindowController; mCommandQueue = commandQueue; @@ -201,6 +211,7 @@ public final class NavBarHelper implements mWm = wm; mDefaultDisplayId = displayTracker.getDefaultDisplayId(); mEdgeBackGestureHandler = edgeBackGestureHandlerFactory.create(context); + mMainExecutor = mainExecutor; mNavBarMode = navigationModeController.addListener(this); mCommandQueue.addCallback(this); @@ -370,7 +381,7 @@ public final class NavBarHelper implements // permission final List<String> a11yButtonTargets = mAccessibilityManager.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_BUTTON); + ShortcutConstants.UserShortcutType.SOFTWARE); final int requestingServices = a11yButtonTargets.size(); clickable = requestingServices >= 1; @@ -418,7 +429,11 @@ public final class NavBarHelper implements @Override public void onConnectionChanged(boolean isConnected) { if (isConnected) { - updateAssistantAvailability(); + // We add the OPS callback during construction, so if the service is already connected + // then we will try to get the AssistManager dependency which itself has an indirect + // dependency on NavBarHelper leading to a cycle. For now, we can defer updating the + // assistant availability. + mMainExecutor.execute(this::updateAssistantAvailability); } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index 93a6eeed3667..b3d848c2d23b 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -371,7 +371,6 @@ internal constructor( // Receiving a CANCEL implies that something else intercepted // the gesture, i.e., the user did not cancel their gesture. // Therefore, disappear immediately, with minimum fanfare. - interactionJankMonitor.cancel(CUJ_BACK_PANEL_ARROW) updateArrowState(GestureState.GONE) velocityTracker = null } @@ -883,16 +882,6 @@ internal constructor( previousState = currentState currentState = newState - // First, update the jank tracker - when (currentState) { - GestureState.ENTRY -> { - interactionJankMonitor.cancel(CUJ_BACK_PANEL_ARROW) - interactionJankMonitor.begin(mView, CUJ_BACK_PANEL_ARROW) - } - GestureState.GONE -> interactionJankMonitor.end(CUJ_BACK_PANEL_ARROW) - else -> {} - } - when (currentState) { GestureState.CANCELLED -> { backCallback.cancelBack() diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 21de185ee838..958ace358816 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -26,8 +26,6 @@ import android.content.res.Configuration; import android.database.ContentObserver; import android.os.BatteryManager; import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.IThermalEventListener; import android.os.IThermalService; import android.os.PowerManager; @@ -97,7 +95,6 @@ public class PowerUI implements private Future mLastShowWarningTask; private boolean mEnableSkinTemperatureWarning; private boolean mEnableUsbTemperatureAlarm; - private final HandlerThread mHandlerThread; private int mLowBatteryAlertCloseLevel; private final int[] mLowBatteryReminderLevels = new int[2]; @@ -170,8 +167,6 @@ public class PowerUI implements mPowerManager = powerManager; mWakefulnessLifecycle = wakefulnessLifecycle; mUserTracker = userTracker; - mHandlerThread = new HandlerThread("PowerUI"); - mHandlerThread.start(); } public void start() { @@ -190,8 +185,7 @@ public class PowerUI implements false, obs, UserHandle.USER_ALL); updateBatteryWarningLevels(); mReceiver.init(); - mUserTracker.addCallback(mUserChangedCallback, - new HandlerExecutor(mHandlerThread.getThreadHandler())); + mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); // Check to see if we need to let the user know that the phone previously shut down due diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index a9dd25bc403d..7d86a6a1794a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -16,6 +16,7 @@ package com.android.systemui.recents; +import static android.app.Flags.keyguardPrivateNotifications; import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; @@ -81,6 +82,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ScreenshotHelper; import com.android.internal.util.ScreenshotRequest; import com.android.systemui.Dumpable; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; @@ -167,13 +169,16 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final Optional<UnfoldTransitionProgressForwarder> mUnfoldTransitionProgressForwarder; private final UiEventLogger mUiEventLogger; private final DisplayTracker mDisplayTracker; - private Region mActiveNavBarRegion; + private final BroadcastDispatcher mBroadcastDispatcher; + private IOverviewProxy mOverviewProxy; private int mConnectionBackoffAttempts; private boolean mBound; private boolean mIsEnabled; + + private boolean mIsNonPrimaryUser; private int mCurrentBoundedUserId = -1; private boolean mInputFocusTransferStarted; private float mInputFocusTransferStartY; @@ -419,6 +424,21 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis retryConnectionWithBackoff(); }; + private final BroadcastReceiver mUserEventReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Objects.equals(intent.getAction(), Intent.ACTION_USER_UNLOCKED)) { + if (keyguardPrivateNotifications()) { + // Start the overview connection to the launcher service + // Connect if user hasn't connected yet + if (getProxy() == null) { + startConnectionToCurrentUser(); + } + } + } + } + }; + private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -493,10 +513,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis notifySystemUiStateFlags(mSysUiState.getFlags()); notifyConnectionChanged(); - if (mDoneUserChanging != null) { - mDoneUserChanging.run(); - mDoneUserChanging = null; - } } @Override @@ -551,14 +567,11 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } }; - private Runnable mDoneUserChanging; private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override - public void onUserChanging(int newUser, @NonNull Context userContext, - @NonNull Runnable resultCallback) { + public void onUserChanged(int newUser, @NonNull Context userContext) { mConnectionBackoffAttempts = 0; - mDoneUserChanging = resultCallback; internalConnectToCurrentUser("User changed"); } }; @@ -586,11 +599,13 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis FeatureFlags featureFlags, SceneContainerFlags sceneContainerFlags, DumpManager dumpManager, - Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder + Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder, + BroadcastDispatcher broadcastDispatcher ) { // b/241601880: This component shouldn't be running for a non-primary user - if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) { - Log.e(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable()); + mIsNonPrimaryUser = !Process.myUserHandle().equals(UserHandle.SYSTEM); + if (mIsNonPrimaryUser) { + Log.wtf(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable()); } mContext = context; @@ -615,6 +630,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mUiEventLogger = uiEventLogger; mDisplayTracker = displayTracker; mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder; + mBroadcastDispatcher = broadcastDispatcher; if (!KeyguardWmStateRefactor.isEnabled()) { mSysuiUnlockAnimationController = sysuiUnlockAnimationController; @@ -635,6 +651,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis filter.addAction(Intent.ACTION_PACKAGE_CHANGED); mContext.registerReceiver(mLauncherStateChangedReceiver, filter); + if (keyguardPrivateNotifications()) { + mBroadcastDispatcher.registerReceiver(mUserEventReceiver, + new IntentFilter(Intent.ACTION_USER_UNLOCKED), + null /* executor */, UserHandle.ALL); + } + // Listen for status bar state changes statusBarWinController.registerCallback(mStatusBarWindowCallback); mScreenshotHelper = new ScreenshotHelper(context); @@ -772,6 +794,13 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } private void internalConnectToCurrentUser(String reason) { + if (mIsNonPrimaryUser) { + // This should not happen, but if any per-user SysUI component has a dependency on OPS, + // then this could get triggered + Log.w(TAG_OPS, "Skipping connection to overview service due to non-primary user " + + "caller"); + return; + } disconnectFromLauncherService(reason); // If user has not setup yet or already connected, do not try to connect diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index b3d2e0918db6..b9e9fe7684e9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -171,28 +171,6 @@ constructor( return repository.setVisible(isVisible) } - /** True if there is a transition happening from and to the specified scenes. */ - fun transitioning(from: SceneKey, to: SceneKey): StateFlow<Boolean> { - fun transitioning( - state: ObservableTransitionState, - from: SceneKey, - to: SceneKey, - ): Boolean { - return (state as? ObservableTransitionState.Transition)?.let { - it.fromScene == from && it.toScene == to - } - ?: false - } - - return transitionState - .map { state -> transitioning(state, from, to) } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = transitioning(transitionState.value, from, to), - ) - } - /** * Binds the given flow so the system remembers it. * 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 995059dfaa9f..246ccb1180b0 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 @@ -30,6 +30,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.model.SceneContainerPlugin import com.android.systemui.model.SysUiState import com.android.systemui.model.updateFlags import com.android.systemui.power.domain.interactor.PowerInteractor @@ -39,11 +40,6 @@ import com.android.systemui.scene.shared.logger.SceneLogger import com.android.systemui.scene.shared.model.ObservableTransitionState import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel -import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING -import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED -import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE -import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED -import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled import com.android.systemui.util.asIndenting @@ -291,12 +287,10 @@ constructor( .collect { sceneKey -> sysUiState.updateFlags( displayId, - SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to (sceneKey != SceneKey.Gone), - SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to (sceneKey == SceneKey.Shade), - SYSUI_STATE_QUICK_SETTINGS_EXPANDED to (sceneKey == SceneKey.QuickSettings), - SYSUI_STATE_BOUNCER_SHOWING to (sceneKey == SceneKey.Bouncer), - SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to - (sceneKey == SceneKey.Lockscreen), + *SceneContainerPlugin.EvaluatorByFlag.map { (flag, evaluator) -> + flag to evaluator.invoke(sceneKey) + } + .toTypedArray(), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt index 93cfc5dbcbe3..2b978b2375d9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt @@ -97,6 +97,10 @@ object SceneWindowRootViewBinder { val legacyView = view.requireViewById<View>(R.id.legacy_window_root) view.addView(createVisibilityToggleView(legacyView)) + // This moves the SharedNotificationContainer to the WindowRootView just after + // the SceneContainerView. This SharedNotificationContainer should contain NSSL + // due to the NotificationStackScrollLayoutSection (legacy) or + // NotificationSection (scene container) moving it there. if (flags.flexiNotifsEnabled()) { (sharedNotificationContainer.parent as? ViewGroup)?.removeView( sharedNotificationContainer diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 97ec3f98cf0c..1c7cc007cbc7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -34,7 +34,7 @@ import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.util.kotlin.collectFlow import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow /** * Controller that's responsible for the glanceable hub container view and its touch handling. @@ -110,10 +110,8 @@ constructor( return communalInteractor.isCommunalEnabled && isComposeAvailable() } - /** Returns a {@link StateFlow} that tracks whether communal hub is enabled. */ - fun enabledState(): StateFlow<Boolean> { - return communalInteractor.communalEnabledState - } + /** Returns a {@link StateFlow} that tracks whether communal hub is available. */ + fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable /** * Creates the container view containing the glanceable hub UI. diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 530c124f4f24..09e4e751c5f0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -66,7 +66,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Trace; import android.os.UserManager; -import android.os.VibrationEffect; import android.provider.Settings; import android.util.IndentingPrintWriter; import android.util.Log; @@ -256,12 +255,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE); private static final boolean DEBUG_DRAWABLE = false; - private static final VibrationEffect ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT = - VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false); /** The parallax amount of the quick settings translation when dragging down the panel. */ public static final float QS_PARALLAX_AMOUNT = 0.175f; - /** The delay to reset the hint text when the hint animation is finished running. */ - private static final int HINT_RESET_DELAY_MS = 1200; private static final long ANIMATION_DELAY_ICON_FADE_IN = ActivityLaunchAnimator.TIMINGS.getTotalDuration() - CollapsedStatusBarFragment.FADE_IN_DURATION @@ -621,7 +616,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private int mDreamingToLockscreenTransitionTranslationY; private int mLockscreenToDreamingTransitionTranslationY; private int mGoneToDreamingTransitionTranslationY; - private SplitShadeStateController mSplitShadeStateController; + private final SplitShadeStateController mSplitShadeStateController; private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */, mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */); private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = @@ -636,9 +631,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } }; - private final Consumer<Boolean> mMultiShadeExpansionConsumer = - (Boolean expanded) -> mIsAnyMultiShadeExpanded = expanded; - private final Consumer<TransitionStep> mDreamingToLockscreenTransition = (TransitionStep step) -> { mIsOcclusionTransitionRunning = diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 5ecc54b09806..19a58401cbec 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -607,7 +607,7 @@ public class NotificationShadeWindowViewController implements Dumpable { public void setupCommunalHubLayout() { collectFlow( mView, - mGlanceableHubContainerController.enabledState(), + mGlanceableHubContainerController.communalAvailable(), isEnabled -> { if (isEnabled) { View communalPlaceholder = mView.findViewById(R.id.communal_ui_stub); diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt index 3fd070c7146e..08f2c402f9e9 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt @@ -109,7 +109,7 @@ constructor( * Returns a flow that uses scene transition progress to and from a scene that is pulled down * from the top of the screen to a 0-1 expansion amount float. */ - internal fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = + fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = sceneInteractor.transitionState .flatMapLatest { state -> when (state) { @@ -135,7 +135,7 @@ constructor( * Returns a flow that uses scene transition data to determine whether the user is interacting * with a scene that is pulled down from the top of the screen. */ - internal fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = + fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) = sceneInteractor.transitionState .map { state -> when (state) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt index 51276c6560a4..314637e4b27e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt @@ -22,12 +22,11 @@ import android.content.IntentFilter import android.icu.text.DateFormat import android.icu.text.DisplayContext import android.os.UserHandle -import com.android.systemui.res.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel import java.util.Date @@ -38,7 +37,6 @@ 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.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -57,16 +55,6 @@ constructor( val mobileIconsViewModel: MobileIconsViewModel, broadcastDispatcher: BroadcastDispatcher, ) { - /** True if we are transitioning between Shade and QuickSettings scenes, in either direction. */ - val isTransitioning = - combine( - sceneInteractor.transitioning(from = SceneKey.Shade, to = SceneKey.QuickSettings), - sceneInteractor.transitioning(from = SceneKey.QuickSettings, to = SceneKey.Shade) - ) { shadeToQuickSettings, quickSettingsToShade -> - shadeToQuickSettings || quickSettingsToShade - } - .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false) - /** True if there is exactly one mobile connection. */ val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index d6d3e6791074..04d9b0cbd428 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -591,8 +591,10 @@ public class KeyguardIndicationController { } private void updateLockScreenUserLockedMsg(int userId) { - if (!mKeyguardUpdateMonitor.isUserUnlocked(userId) - || mKeyguardUpdateMonitor.isEncryptedOrLockdown(userId)) { + boolean userUnlocked = mKeyguardUpdateMonitor.isUserUnlocked(userId); + boolean encryptedOrLockdown = mKeyguardUpdateMonitor.isEncryptedOrLockdown(userId); + mKeyguardLogger.logUpdateLockScreenUserLockedMsg(userId, userUnlocked, encryptedOrLockdown); + if (!userUnlocked || encryptedOrLockdown) { mRotateTextViewController.updateIndication( INDICATION_TYPE_USER_LOCKED, new KeyguardIndication.Builder() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 24ac70e63e46..2a4753def463 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -234,10 +234,12 @@ public class NotificationLockscreenUserManagerImpl implements } else if (profileAvailabilityActions(action)) { updateCurrentProfilesCache(); } else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) { - // Start the overview connection to the launcher service - // Connect if user hasn't connected yet - if (mOverviewProxyServiceLazy.get().getProxy() == null) { - mOverviewProxyServiceLazy.get().startConnectionToCurrentUser(); + if (!keyguardPrivateNotifications()) { + // Start the overview connection to the launcher service + // Connect if user hasn't connected yet + if (mOverviewProxyServiceLazy.get().getProxy() == null) { + mOverviewProxyServiceLazy.get().startConnectionToCurrentUser(); + } } } else if (Objects.equals(action, NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION)) { final IntentSender intentSender = intent.getParcelableExtra( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 28d4457b264b..fc84973c46bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -35,6 +35,7 @@ import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.provider.Settings; import android.telephony.CarrierConfigManager; @@ -60,6 +61,7 @@ import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults; import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.DataUsageController; import com.android.systemui.Dumpable; +import com.android.systemui.res.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -71,7 +73,6 @@ import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.LogLevel; import com.android.systemui.log.dagger.StatusBarNetworkControllerLog; import com.android.systemui.qs.tiles.dialog.InternetDialogFactory; -import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -84,8 +85,6 @@ import com.android.systemui.util.CarrierConfigTracker; import dalvik.annotation.optimization.NeverCompile; -import kotlin.Unit; - import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -100,6 +99,8 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import kotlin.Unit; + /** Platform implementation of the network controller. **/ @SysUISingleton public class NetworkControllerImpl extends BroadcastReceiver @@ -349,7 +350,7 @@ public class NetworkControllerImpl extends BroadcastReceiver // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it updateAirplaneMode(true /* force callback */); mUserTracker = userTracker; - mUserTracker.addCallback(mUserChangedCallback, mBgExecutor); + mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler)); deviceProvisionedController.addCallback(new DeviceProvisionedListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index 342828c4b5d3..aca8b64c05d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -6,7 +6,6 @@ import android.database.ContentObserver import android.net.Uri import android.os.Handler import android.os.HandlerExecutor -import android.os.HandlerThread import android.os.UserHandle import android.provider.Settings import com.android.keyguard.KeyguardUpdateMonitor @@ -88,7 +87,6 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor( secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS) private val onStateChangedListeners = ListenerSet<Consumer<String>>() private var hideSilentNotificationsOnLockscreen: Boolean = false - private val handlerThread: HandlerThread = HandlerThread("KeyguardNotificationVis") private val userTrackerCallback = object : UserTracker.Callback { override fun onUserChanged(newUser: Int, userContext: Context) { @@ -156,9 +154,7 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor( notifyStateChanged("onStatusBarUpcomingStateChanged") } }) - handlerThread.start() - userTracker.addCallback(userTrackerCallback, - HandlerExecutor(handlerThread.getThreadHandler())) + userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler)) } override fun addOnStateChangedListener(listener: Consumer<String>) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java index 3443da19895e..99a6f6a59bd0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java @@ -45,13 +45,15 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; + import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.res.R; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; +import com.android.systemui.res.R; import java.util.ArrayList; import java.util.List; @@ -77,6 +79,9 @@ public class NotificationSnooze extends LinearLayout private static final LogMaker UNDO_LOG = new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE) .setType(MetricsEvent.TYPE_ACTION); + + private static final String PARAGRAPH_SEPARATOR = "\u2029"; + private NotificationGuts mGutsContainer; private NotificationSwipeActionHelper mSnoozeListener; private StatusBarNotification mSbn; @@ -111,8 +116,7 @@ public class NotificationSnooze extends LinearLayout } @VisibleForTesting - SnoozeOption getDefaultOption() - { + SnoozeOption getDefaultOption() { return mDefaultOption; } @@ -130,6 +134,8 @@ public class NotificationSnooze extends LinearLayout mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default); mUndoButton = (TextView) findViewById(R.id.undo); mUndoButton.setOnClickListener(this); + mUndoButton.setContentDescription( + getContext().getString(R.string.snooze_undo_content_description)); mExpandButton = (ImageView) findViewById(R.id.expand_button); mDivider = findViewById(R.id.divider); mDivider.setAlpha(0f); @@ -163,6 +169,46 @@ public class NotificationSnooze extends LinearLayout info.addAction(action); } } + + mSnoozeView.setAccessibilityDelegate(new AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + // Replace "Double tap to activate" prompt with "Double tap to expand/collapse" + AccessibilityAction customClick = new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLICK, getExpandActionString()); + info.addAction(customClick); + } + }); + } + + /** + * Update the content description of the snooze view based on the snooze option and whether the + * snooze options are expanded or not. + * For example, this will be something like "Collapsed\u2029Snooze for 1 hour". The paragraph + * separator is added to introduce a break in speech, to match what TalkBack does by default + * when you e.g. press on a notification. + */ + private void updateContentDescription() { + mSnoozeView.setContentDescription( + getExpandStateString() + PARAGRAPH_SEPARATOR + mSelectedOptionText.getText()); + } + + /** Returns "collapse" if the snooze options are expanded, or "expand" otherwise. */ + @NonNull + private String getExpandActionString() { + return mContext.getString(mExpanded + ? com.android.internal.R.string.expand_button_content_description_expanded + : com.android.internal.R.string.expand_button_content_description_collapsed); + } + + + /** Returns "expanded" if the snooze options are expanded, or "collapsed" otherwise. */ + @NonNull + private String getExpandStateString() { + return mContext.getString( + (mExpanded ? com.android.internal.R.string.content_description_expanded + : com.android.internal.R.string.content_description_collapsed)); } @Override @@ -179,6 +225,8 @@ public class NotificationSnooze extends LinearLayout if (so.getAccessibilityAction() != null && so.getAccessibilityAction().getId() == action) { setSelected(so, true); + mSnoozeView.sendAccessibilityEvent( + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); return true; } } @@ -290,11 +338,10 @@ public class NotificationSnooze extends LinearLayout int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification : com.android.internal.R.drawable.ic_expand_notification; mExpandButton.setImageResource(drawableId); - mExpandButton.setContentDescription(mContext.getString(show - ? com.android.internal.R.string.expand_button_content_description_expanded - : com.android.internal.R.string.expand_button_content_description_collapsed)); + mExpandButton.setContentDescription(getExpandActionString()); if (mExpanded != show) { mExpanded = show; + updateContentDescription(); animateSnoozeOptions(show); if (mGutsContainer != null) { mGutsContainer.onHeightChanged(); @@ -335,8 +382,11 @@ public class NotificationSnooze extends LinearLayout } private void setSelected(SnoozeOption option, boolean userAction) { - mSelectedOption = option; - mSelectedOptionText.setText(option.getConfirmation()); + if (option != mSelectedOption) { + mSelectedOption = option; + mSelectedOptionText.setText(option.getConfirmation()); + updateContentDescription(); + } showSnoozeOptions(false); hideSelectedOption(); if (userAction) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 7952511addfe..64fcef51755d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -28,10 +28,10 @@ import static androidx.lifecycle.Lifecycle.State.RESUMED; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import static com.android.systemui.Flags.lightRevealMigration; +import static com.android.systemui.Flags.predictiveBackSysui; import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL; import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; import static com.android.systemui.statusbar.StatusBarState.SHADE; -import static com.android.systemui.Flags.predictiveBackSysui; import android.annotation.Nullable; import android.app.ActivityOptions; @@ -95,7 +95,6 @@ import androidx.lifecycle.LifecycleRegistry; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -126,6 +125,7 @@ import com.android.systemui.charging.WiredChargingRippleController; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -202,12 +202,10 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.data.model.StatusBarMode; import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; -import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.init.NotificationsController; -import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor; @@ -225,7 +223,6 @@ import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; -import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape; @@ -249,6 +246,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Executor; +import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; @@ -278,19 +276,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private static final String BANNER_ACTION_SETUP = "com.android.systemui.statusbar.banner_action_setup"; - private static final int MSG_OPEN_SETTINGS_PANEL = 1002; private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003; // 1020-1040 reserved for BaseStatusBar - /** - * TODO(b/249277686) delete this - * The delay to reset the hint text when the hint animation is finished running. - */ - private static final int HINT_RESET_DELAY_MS = 1200; - - /** If true, the lockscreen will show a distinct wallpaper */ - public static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true; - private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl(); private final Context mContext; @@ -300,7 +288,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks; private float mTransitionToFullShadeProgress = 0f; private final NotificationListContainer mNotifListContainer; - private boolean mIsShortcutListSearchEnabled; + private final boolean mIsShortcutListSearchEnabled; private final KeyguardStateController.Callback mKeyguardStateControllerCallback = new KeyguardStateController.Callback() { @@ -459,7 +447,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final NotificationGutsManager mGutsManager; private final ShadeExpansionStateManager mShadeExpansionStateManager; private final KeyguardViewMediator mKeyguardViewMediator; - private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider; private final BrightnessSliderController.Factory mBrightnessSliderFactory; private final FeatureFlags mFeatureFlags; private final FragmentService mFragmentService; @@ -555,7 +542,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private int mLastLoggedStateFingerprint; private boolean mIsLaunchingActivityOverLockscreen; - private final UserSwitcherController mUserSwitcherController; private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); protected final BatteryController mBatteryController; private UiModeManager mUiModeManager; @@ -567,6 +553,25 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final WakefulnessLifecycle mWakefulnessLifecycle; protected final PowerInteractor mPowerInteractor; + private final CommunalInteractor mCommunalInteractor; + + /** + * True if the device is showing the glanceable hub. See + * {@link CommunalInteractor#isIdleOnCommunal()} for more details. + */ + private boolean mIsIdleOnCommunal = false; + private final Consumer<Boolean> mIdleOnCommunalConsumer = (Boolean idleOnCommunal) -> { + if (idleOnCommunal == mIsIdleOnCommunal) { + // Ignore initial value coming through the flow. + return; + } + + mIsIdleOnCommunal = idleOnCommunal; + // Trigger an update for the scrim state when we enter or exit glanceable hub, so that we + // can transition to/from ScrimState.GLANCEABLE_HUB if needed. + updateScrimController(); + }; + private boolean mNoAnimationOnNextBarModeChange; private final SysuiStatusBarStateController mStatusBarStateController; @@ -586,8 +591,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener = (extractor, which) -> updateTheme(); - private final InteractionJankMonitor mJankMonitor; - private final SceneContainerFlags mSceneContainerFlags; /** @@ -615,12 +618,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { KeyguardBypassController keyguardBypassController, KeyguardStateController keyguardStateController, HeadsUpManager headsUpManager, - DynamicPrivacyController dynamicPrivacyController, FalsingManager falsingManager, FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, NotificationGutsManager notificationGutsManager, - VisualInterruptionDecisionProvider visualInterruptionDecisionProvider, ShadeExpansionStateManager shadeExpansionStateManager, KeyguardViewMediator keyguardViewMediator, DisplayMetrics displayMetrics, @@ -633,12 +634,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { NotificationLockscreenUserManager lockScreenUserManager, NotificationRemoteInputManager remoteInputManager, QuickSettingsController quickSettingsController, - UserSwitcherController userSwitcherController, BatteryController batteryController, SysuiColorExtractor colorExtractor, ScreenLifecycle screenLifecycle, WakefulnessLifecycle wakefulnessLifecycle, PowerInteractor powerInteractor, + CommunalInteractor communalInteractor, SysuiStatusBarStateController statusBarStateController, Optional<Bubbles> bubblesOptional, Lazy<NoteTaskController> noteTaskControllerLazy, @@ -693,7 +694,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { WallpaperManager wallpaperManager, Optional<StartingSurface> startingSurfaceOptional, ActivityLaunchAnimator activityLaunchAnimator, - InteractionJankMonitor jankMonitor, DeviceStateManager deviceStateManager, WiredChargingRippleController wiredChargingRippleController, IDreamManager dreamManager, @@ -727,7 +727,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mFalsingManager = falsingManager; mBroadcastDispatcher = broadcastDispatcher; mGutsManager = notificationGutsManager; - mVisualInterruptionDecisionProvider = visualInterruptionDecisionProvider; mShadeExpansionStateManager = shadeExpansionStateManager; mKeyguardViewMediator = keyguardViewMediator; mDisplayMetrics = displayMetrics; @@ -740,12 +739,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mLockscreenUserManager = lockScreenUserManager; mRemoteInputManager = remoteInputManager; mQsController = quickSettingsController; - mUserSwitcherController = userSwitcherController; mBatteryController = batteryController; mColorExtractor = colorExtractor; mScreenLifecycle = screenLifecycle; mWakefulnessLifecycle = wakefulnessLifecycle; mPowerInteractor = powerInteractor; + mCommunalInteractor = communalInteractor; mStatusBarStateController = statusBarStateController; mBubblesOptional = bubblesOptional; mNoteTaskControllerLazy = noteTaskControllerLazy; @@ -795,7 +794,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mMainExecutor = delayableExecutor; mMessageRouter = messageRouter; mWallpaperManager = wallpaperManager; - mJankMonitor = jankMonitor; mCameraLauncherLazy = cameraLauncherLazy; mAlternateBouncerInteractor = alternateBouncerInteractor; mUserTracker = userTracker; @@ -1076,6 +1074,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { //TODO(b/264502026) move the rest of the listeners here. mDeviceStateManager.registerCallback(mMainExecutor, new FoldStateListener(mContext, this::onFoldedStateChanged)); + + mJavaAdapter.alwaysCollectFlow( + mCommunalInteractor.isIdleOnCommunal(), + mIdleOnCommunalConsumer); } /** @@ -2820,6 +2822,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // This will cancel the keyguardFadingAway animation if it is running. We need to do // this as otherwise it can remain pending and leave keyguard in a weird state. mUnlockScrimCallback.onCancelled(); + } else if (mIsIdleOnCommunal) { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); } else if (mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded() && !unlocking) { @@ -3087,7 +3091,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } }; - private StatusBarStateController.StateListener mStateListener = + private final StatusBarStateController.StateListener mStateListener = new StatusBarStateController.StateListener() { @Override public void onStatePreChange(int oldState, int newState) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt deleted file mode 100644 index 9f0863385ca1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.statusbar.phone - -import android.content.Context -import android.content.pm.PackageManager -import android.hardware.Sensor -import android.hardware.TriggerEvent -import android.hardware.TriggerEventListener -import com.android.keyguard.ActiveUnlockConfig -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.CoreStartable -import com.android.systemui.Dumpable -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dump.DumpManager -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.user.domain.interactor.SelectedUserInteractor -import com.android.systemui.util.Assert -import com.android.systemui.util.sensors.AsyncSensorManager -import java.io.PrintWriter -import javax.inject.Inject - -/** - * Triggers face auth on lift when the device is showing the lock screen. Only initialized - * if face auth is supported on the device. Not to be confused with the lift to wake gesture - * which is handled by {@link com.android.server.policy.PhoneWindowManager}. - */ -@SysUISingleton -class KeyguardLiftController @Inject constructor( - private val context: Context, - private val statusBarStateController: StatusBarStateController, - private val asyncSensorManager: AsyncSensorManager, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, - private val dumpManager: DumpManager, - private val selectedUserInteractor: SelectedUserInteractor, -) : Dumpable, CoreStartable { - - private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) - private var isListening = false - private var bouncerVisible = false - - override fun start() { - if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { - init() - } - } - - private fun init() { - dumpManager.registerDumpable(this) - statusBarStateController.addCallback(statusBarStateListener) - keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) - updateListeningState() - } - - private val listener: TriggerEventListener = object : TriggerEventListener() { - override fun onTrigger(event: TriggerEvent?) { - Assert.isMainThread() - // Not listening anymore since trigger events unregister themselves - isListening = false - updateListeningState() - deviceEntryFaceAuthInteractor.onDeviceLifted() - keyguardUpdateMonitor.requestActiveUnlock( - ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, - "KeyguardLiftController") - } - } - - private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { - override fun onKeyguardBouncerFullyShowingChanged(bouncer: Boolean) { - bouncerVisible = bouncer - updateListeningState() - } - - override fun onKeyguardVisibilityChanged(visible: Boolean) { - updateListeningState() - } - } - - private val statusBarStateListener = object : StatusBarStateController.StateListener { - override fun onDozingChanged(isDozing: Boolean) { - updateListeningState() - } - } - - override fun dump(pw: PrintWriter, args: Array<out String>) { - pw.println("KeyguardLiftController:") - pw.println(" pickupSensor: $pickupSensor") - pw.println(" isListening: $isListening") - pw.println(" bouncerVisible: $bouncerVisible") - } - - private fun updateListeningState() { - if (pickupSensor == null) { - return - } - val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && - !statusBarStateController.isDozing - - val isFaceEnabled = deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() - val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled - if (shouldListen != isListening) { - isListening = shouldListen - - if (shouldListen) { - asyncSensorManager.requestTriggerSensor(listener, pickupSensor) - } else { - asyncSensorManager.cancelTriggerSensor(listener, pickupSensor) - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index aabe4a0d66f9..6f78604e996f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER; +import static com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB; import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE; +import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN; import static com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -62,6 +64,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.ScrimAlpha; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -292,6 +295,30 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimBehind.setViewAlpha(mBehindAlpha); }; + /** + * Consumer that fades the behind scrim in and out during the transition between the lock screen + * and the glanceable hub. + * + * While the lock screen is showing, the behind scrim is used to slightly darken the lock screen + * wallpaper underneath. Since the glanceable hub is under all of the scrims, we want to fade + * out the scrim so that the glanceable hub isn't darkened when it opens. + * + * {@link #applyState()} handles the scrim alphas once on the glanceable hub, this is only + * responsible for setting the behind alpha during the transition. + */ + private final Consumer<TransitionStep> mGlanceableHubConsumer = (TransitionStep step) -> { + final float baseAlpha = ScrimState.KEYGUARD.getBehindAlpha(); + final float transitionProgress = step.getValue(); + if (step.getTo() == KeyguardState.LOCKSCREEN) { + // Transitioning back to lock screen, fade in behind scrim again. + mBehindAlpha = baseAlpha * transitionProgress; + } else if (step.getTo() == GLANCEABLE_HUB) { + // Transitioning to glanceable hub, fade out behind scrim. + mBehindAlpha = baseAlpha * (1 - transitionProgress); + } + mScrimBehind.setViewAlpha(mBehindAlpha); + }; + Consumer<TransitionStep> mBouncerToGoneTransition; @Inject @@ -444,6 +471,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mBouncerToGoneTransition, mMainDispatcher); collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(), mScrimAlphaConsumer, mMainDispatcher); + + // LOCKSCREEN<->GLANCEABLE_HUB + collectFlow(behindScrim, + mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB), + mGlanceableHubConsumer, mMainDispatcher); + collectFlow(behindScrim, + mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN), + mGlanceableHubConsumer, mMainDispatcher); } // TODO(b/270984686) recompute scrim height accurately, based on shade contents. @@ -763,10 +798,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump // see: b/186644628 mNotificationsScrim.setDrawableBounds(left - 1, top, right + 1, bottom); mScrimBehind.setBottomEdgePosition((int) top); + } else { + mNotificationsScrim.setDrawableBounds(left, top, right, bottom); + } + + // Only clip if the notif scrim is visible + if (mNotificationsAlpha > 0f) { mKeyguardInteractor.setTopClippingBounds((int) top); } else { mKeyguardInteractor.setTopClippingBounds(null); - mNotificationsScrim.setDrawableBounds(left, top, right, bottom); } } @@ -810,9 +850,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump return; } mBouncerHiddenFraction = bouncerHiddenAmount; - if (mState == ScrimState.DREAMING) { - // Only the dreaming state requires this for the scrim calculation, so we should - // only trigger an update if dreaming. + if (mState == ScrimState.DREAMING || mState == ScrimState.GLANCEABLE_HUB) { + // The dreaming and glanceable hub states requires this for the scrim calculation, so we + // should only trigger an update in those states. applyAndDispatchState(); } } @@ -934,7 +974,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) { mNotificationsAlpha = (float) Math.pow(getInterpolatedFraction(), 0.8f); } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED - || mState == ScrimState.PULSING) { + || mState == ScrimState.PULSING || mState == ScrimState.GLANCEABLE_HUB) { Pair<Integer, Float> result = calculateBackStateForState(mState); int behindTint = result.first; float behindAlpha = result.second; @@ -945,6 +985,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mTransitionToFullShadeProgress); behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first, mTransitionToFullShadeProgress); + } else if (mState == ScrimState.GLANCEABLE_HUB && mTransitionToFullShadeProgress == 0.0f + && mBouncerHiddenFraction == KeyguardBouncerConstants.EXPANSION_HIDDEN) { + // Behind scrim should not be visible when idle on the glanceable hub and neither + // bouncer nor shade are showing. + behindAlpha = 0f; } mInFrontAlpha = mState.getFrontAlpha(); if (mClipsQsScrim) { @@ -960,6 +1005,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (mState == ScrimState.SHADE_LOCKED) { // going from KEYGUARD to SHADE_LOCKED state mNotificationsAlpha = getInterpolatedFraction(); + } else if (mState == ScrimState.GLANCEABLE_HUB + && mTransitionToFullShadeProgress == 0.0f) { + // Notification scrim should not be visible on the glanceable hub unless the + // shade is showing or transitioning in. Otherwise the notification scrim will + // be visible as the bouncer transitions in or after the notification shade + // closes. + mNotificationsAlpha = 0; } else { mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index e3b65ab27f48..f2a649ba2e32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -20,6 +20,7 @@ import android.graphics.Color; import android.os.Trace; import com.android.systemui.dock.DockManager; +import com.android.systemui.res.R; import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -39,8 +40,8 @@ public enum ScrimState { OFF { @Override public void prepare(ScrimState previousState) { - mFrontTint = Color.BLACK; - mBehindTint = Color.BLACK; + mFrontTint = mBackgroundColor; + mBehindTint = mBackgroundColor; mFrontAlpha = 1f; mBehindAlpha = 1f; @@ -74,15 +75,15 @@ public enum ScrimState { } else { mAnimationDuration = ScrimController.ANIMATION_DURATION; } - mFrontTint = Color.BLACK; - mBehindTint = Color.BLACK; - mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; + mFrontTint = mBackgroundColor; + mBehindTint = mBackgroundColor; + mNotifTint = mClipQsScrim ? mBackgroundColor : Color.TRANSPARENT; mFrontAlpha = 0; mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard; mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0; if (mClipQsScrim) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor); } } }, @@ -93,10 +94,10 @@ public enum ScrimState { // notif scrim alpha values are determined by ScrimController#applyState // based on the shade expansion - mFrontTint = Color.BLACK; + mFrontTint = mBackgroundColor; mFrontAlpha = .66f; - mBehindTint = Color.BLACK; + mBehindTint = mBackgroundColor; mBehindAlpha = 1f; } }, @@ -110,7 +111,7 @@ public enum ScrimState { mBehindTint = previousState.mBehindTint; mBehindAlpha = previousState.mBehindAlpha; - mFrontTint = Color.BLACK; + mFrontTint = mBackgroundColor; mFrontAlpha = .66f; } }, @@ -122,7 +123,7 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; - mBehindTint = mClipQsScrim ? Color.BLACK : mSurfaceColor; + mBehindTint = mClipQsScrim ? mBackgroundColor : mSurfaceColor; mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0; mNotifTint = Color.TRANSPARENT; mFrontAlpha = 0f; @@ -154,10 +155,10 @@ public enum ScrimState { mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; mNotifAlpha = 1f; mFrontAlpha = 0f; - mBehindTint = mClipQsScrim ? Color.TRANSPARENT : Color.BLACK; + mBehindTint = mClipQsScrim ? Color.TRANSPARENT : mBackgroundColor; if (mClipQsScrim) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor); } } }, @@ -184,11 +185,11 @@ public enum ScrimState { final boolean isDocked = mDockManager.isDocked(); mBlankScreen = mDisplayRequiresBlanking; - mFrontTint = Color.BLACK; + mFrontTint = mBackgroundColor; mFrontAlpha = (alwaysOnEnabled || isDocked || quickPickupEnabled) ? mAodFrontScrimAlpha : 1f; - mBehindTint = Color.BLACK; + mBehindTint = mBackgroundColor; mBehindAlpha = ScrimController.TRANSPARENT; mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG; @@ -222,8 +223,8 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { mFrontAlpha = mAodFrontScrimAlpha; - mBehindTint = Color.BLACK; - mFrontTint = Color.BLACK; + mBehindTint = mBackgroundColor; + mFrontTint = mBackgroundColor; mBlankScreen = mDisplayRequiresBlanking; mAnimationDuration = mWakeLockScreenSensorActive ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION; @@ -231,7 +232,7 @@ public enum ScrimState { @Override public float getMaxLightRevealScrimAlpha() { return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA - : AOD.getMaxLightRevealScrimAlpha(); + : AOD.getMaxLightRevealScrimAlpha(); } }, @@ -245,7 +246,6 @@ public enum ScrimState { mBehindAlpha = mClipQsScrim ? 1 : 0; mNotifAlpha = 0; mFrontAlpha = 0; - mAnimationDuration = mKeyguardFadingAway ? mKeyguardFadingAwayDuration : CentralSurfaces.FADE_KEYGUARD_DURATION; @@ -259,22 +259,22 @@ public enum ScrimState { && !fromAod; mFrontTint = Color.TRANSPARENT; - mBehindTint = Color.BLACK; + mBehindTint = mBackgroundColor; mBlankScreen = false; if (mDisplayRequiresBlanking && previousState == ScrimState.AOD) { // Set all scrims black, before they fade transparent. - updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */); - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */); + updateScrimColor(mScrimInFront, 1f /* alpha */, mBackgroundColor /* tint */); + updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor /* tint */); // Scrims should still be black at the end of the transition. - mFrontTint = Color.BLACK; - mBehindTint = Color.BLACK; + mFrontTint = mBackgroundColor; + mBehindTint = mBackgroundColor; mBlankScreen = true; } if (mClipQsScrim) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor); } } }, @@ -283,8 +283,8 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { mFrontTint = Color.TRANSPARENT; - mBehindTint = Color.BLACK; - mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; + mBehindTint = mBackgroundColor; + mNotifTint = mClipQsScrim ? mBackgroundColor : Color.TRANSPARENT; mFrontAlpha = 0; mBehindAlpha = mClipQsScrim ? 1 : 0; @@ -293,9 +293,24 @@ public enum ScrimState { mBlankScreen = false; if (mClipQsScrim) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor); } } + }, + + /** + * Device is locked or on dream and user has swiped from the right edge to enter the glanceable + * hub UI. From this state, the user can swipe from the left edge to go back to the lock screen + * or dream, as well as swipe down for the notifications and up for the bouncer. + */ + GLANCEABLE_HUB { + @Override + public void prepare(ScrimState previousState) { + // No scrims should be visible by default in this state. + mBehindAlpha = 0; + mNotifAlpha = 0; + mFrontAlpha = 0; + } }; boolean mBlankScreen = false; @@ -327,9 +342,11 @@ public enum ScrimState { boolean mKeyguardFadingAway; long mKeyguardFadingAwayDuration; boolean mClipQsScrim; + int mBackgroundColor; public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters, DockManager dockManager) { + mBackgroundColor = scrimBehind.getContext().getColor(R.color.shade_scrim_background_dark); mScrimInFront = scrimInFront; mScrimBehind = scrimBehind; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt index 0e6775685611..f73d089c36b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt @@ -153,7 +153,14 @@ constructor( if (satelliteManager != null) { // First, check that satellite is supported on this device scope.launch { - ensureMinUptime(systemClock, MIN_UPTIME) + val waitTime = ensureMinUptime(systemClock, MIN_UPTIME) + if (waitTime > 0) { + logBuffer.i({ long1 = waitTime }) { + "Waiting $long1 ms before checking for satellite support" + } + delay(waitTime) + } + satelliteSupport.value = satelliteManager.checkSatelliteSupported() logBuffer.i( @@ -176,7 +183,7 @@ constructor( */ while (true) { logBuffer.i { - "requestIsSatelliteCommunicationAllowedForCurrentLocation" + "requestIsCommunicationAllowedForCurrentLocation" } checkIsSatelliteAllowed() delay(POLLING_INTERVAL_MS) @@ -209,14 +216,13 @@ constructor( var registered = false try { - val res = - sm.registerForSatelliteModemStateChanged(bgDispatcher.asExecutor(), cb) + val res = sm.registerForModemStateChanged(bgDispatcher.asExecutor(), cb) registered = res == SATELLITE_RESULT_SUCCESS } catch (e: Exception) { logBuffer.e("error registering for modem state", e) } - awaitClose { if (registered) sm.unregisterForSatelliteModemStateChanged(cb) } + awaitClose { if (registered) sm.unregisterForModemStateChanged(cb) } } .flowOn(bgDispatcher) @@ -248,7 +254,7 @@ constructor( /** Fire off a request to check for satellite availability. Always runs on the bg context */ private suspend fun checkIsSatelliteAllowed() = withContext(bgDispatcher) { - satelliteManager?.requestIsSatelliteCommunicationAllowedForCurrentLocation( + satelliteManager?.requestIsCommunicationAllowedForCurrentLocation( bgDispatcher.asExecutor(), object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> { override fun onError(e: SatelliteManager.SatelliteException) { @@ -260,7 +266,7 @@ constructor( } override fun onResult(allowed: Boolean) { - logBuffer.i { allowed.toString() } + logBuffer.i { "isSatelliteAllowedForCurrentLocation: $allowed" } isSatelliteAllowedForCurrentLocation.value = allowed } } @@ -294,7 +300,7 @@ constructor( } try { - requestIsSatelliteSupported(bgDispatcher.asExecutor(), cb) + requestIsSupported(bgDispatcher.asExecutor(), cb) } catch (error: Exception) { logBuffer.e( "Exception when checking for satellite support. " + @@ -309,19 +315,14 @@ constructor( // TTL for satellite polling is one hour const val POLLING_INTERVAL_MS: Long = 1000 * 60 * 60 - // Let the system boot up and stabilize before we check for system support - const val MIN_UPTIME: Long = 1000 * 60 + // Let the system boot up (5s) and stabilize before we check for system support + const val MIN_UPTIME: Long = 1000 * 5 private const val TAG = "DeviceBasedSatelliteRepo" - /** If our process hasn't been up for at least MIN_UPTIME, delay until we reach that time */ - private suspend fun ensureMinUptime(clock: SystemClock, uptime: Long) { - val timeTilMinUptime = - uptime - (clock.uptimeMillis() - android.os.Process.getStartUptimeMillis()) - if (timeTilMinUptime > 0) { - delay(timeTilMinUptime) - } - } + /** Calculates how long we have to wait to reach MIN_UPTIME */ + private fun ensureMinUptime(clock: SystemClock, uptime: Long): Long = + uptime - (clock.uptimeMillis() - android.os.Process.getStartUptimeMillis()) /** A couple of convenience logging methods rather than a whole class */ private fun LogBuffer.i( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt index 877957733d67..6e1114c57e87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt @@ -79,7 +79,7 @@ constructor( } else { flowOf(false) } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + .stateIn(scope, SharingStarted.WhileSubscribed(), true) } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index a2d8d1579e3d..20d1fff91443 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -28,8 +28,6 @@ import android.icu.lang.UCharacter; import android.icu.text.DateTimePatternGenerator; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.Parcelable; import android.os.SystemClock; import android.os.UserHandle; @@ -50,11 +48,11 @@ import android.widget.TextView; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; +import com.android.systemui.res.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -108,7 +106,6 @@ public class Clock extends TextView implements private final int mAmPmStyle; private boolean mShowSeconds; private Handler mSecondsHandler; - private HandlerThread mHandlerThread; // Fields to cache the width so the clock remains at an approximately constant width private int mCharsAtCurrentWidth = -1; @@ -149,8 +146,6 @@ public class Clock extends TextView implements } mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class); mUserTracker = Dependency.get(UserTracker.class); - mHandlerThread = new HandlerThread("Clock"); - mHandlerThread.start(); setIncludeFontPadding(false); } @@ -210,8 +205,7 @@ public class Clock extends TextView implements Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS, StatusBarIconController.ICON_HIDE_LIST); mCommandQueue.addCallback(this); - mUserTracker.addCallback(mUserChangedCallback, - new HandlerExecutor(mHandlerThread.getThreadHandler())); + mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); mCurrentUserId = mUserTracker.getUserId(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java index a7440d6c200e..b7d8ee3943e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java @@ -21,8 +21,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.UserHandle; import androidx.annotation.NonNull; @@ -53,7 +51,6 @@ public class NextAlarmControllerImpl extends BroadcastReceiver private final UserTracker mUserTracker; private AlarmManager mAlarmManager; private AlarmManager.AlarmClockInfo mNextAlarm; - private HandlerThread mHandlerThread; private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @@ -78,10 +75,7 @@ public class NextAlarmControllerImpl extends BroadcastReceiver IntentFilter filter = new IntentFilter(); filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); broadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL); - mHandlerThread = new HandlerThread("NextAlarmControllerImpl"); - mHandlerThread.start(); - mUserTracker.addCallback(mUserChangedCallback, - new HandlerExecutor(mHandlerThread.getThreadHandler())); + mUserTracker.addCallback(mUserChangedCallback, mainExecutor); updateNextAlarm(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 6a6efbc11362..9f4a90658b2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -157,7 +157,7 @@ public class SecurityControllerImpl implements SecurityController { // TODO: re-register network callback on user change. mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback); onUserSwitched(mUserTracker.getUserId()); - mUserTracker.addCallback(mUserChangedCallback, mBgExecutor); + mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); } public void dump(PrintWriter pw, String[] args) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java index 0bc0e88114a5..2ed9d1548007 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java @@ -36,9 +36,9 @@ import androidx.annotation.NonNull; import com.android.internal.util.UserIcons; import com.android.settingslib.drawable.UserIconDrawable; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.res.R; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.UserTracker; import java.util.ArrayList; @@ -66,11 +66,11 @@ public class UserInfoControllerImpl implements UserInfoController { /** */ @Inject - public UserInfoControllerImpl(Context context, @Background Executor bgExecutor, + public UserInfoControllerImpl(Context context, @Main Executor mainExecutor, UserTracker userTracker) { mContext = context; mUserTracker = userTracker; - mUserTracker.addCallback(mUserChangedCallback, bgExecutor); + mUserTracker.addCallback(mUserChangedCallback, mainExecutor); IntentFilter profileFilter = new IntentFilter(); profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED); 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 f0b49307aad5..df210b073e77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -29,7 +29,6 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; @@ -82,7 +81,6 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable { private volatile int mZenMode; private long mZenUpdateTime; private NotificationManager.Policy mConsolidatedNotificationPolicy; - private HandlerThread mHandlerThread; private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @@ -135,8 +133,6 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable { } } }; - mHandlerThread = new HandlerThread("ZenModeControllerImpl"); - mHandlerThread.start(); mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver); updateZenMode(getModeSettingValueFromProvider()); @@ -147,8 +143,7 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable { mSetupObserver = new SetupObserver(handler); mSetupObserver.register(); mUserManager = context.getSystemService(UserManager.class); - mUserTracker.addCallback(mUserChangedCallback, - new HandlerExecutor(mHandlerThread.getThreadHandler())); + mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(handler)); // This registers the alarm broadcast receiver for the current user mUserChangedCallback.onUserChanged(getCurrentUser(), context); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 77518db9184c..2b9ad50c1257 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -480,7 +480,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { return; } - mUserTracker.addCallback(mUserTrackerCallback, mBgExecutor); + mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor); mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index f5b4d17ae7d3..550a65c01bfc 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -26,7 +26,6 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.Looper; import android.os.UserManager; import android.provider.Settings; @@ -39,11 +38,11 @@ import androidx.annotation.WorkerThread; import com.android.internal.util.ArrayUtils; import com.android.systemui.DejankUtils; +import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.qs.QSHost; -import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.SystemUIDialog; @@ -99,7 +98,6 @@ public class TunerServiceImpl extends TunerService { private UserTracker.Callback mCurrentUserTracker; private UserTracker mUserTracker; private final ComponentName mTunerComponent; - private HandlerThread mHandlerThread; /** */ @@ -119,8 +117,7 @@ public class TunerServiceImpl extends TunerService { mDemoModeController = demoModeController; mUserTracker = userTracker; mTunerComponent = new ComponentName(mContext, TunerActivity.class); - mHandlerThread = new HandlerThread("TunerServiceImpl"); - mHandlerThread.start(); + for (UserInfo user : UserManager.get(mContext).getUsers()) { mCurrentUser = user.getUserHandle().getIdentifier(); if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) { @@ -138,7 +135,7 @@ public class TunerServiceImpl extends TunerService { } }; mUserTracker.addCallback(mCurrentUserTracker, - new HandlerExecutor(mHandlerThread.getThreadHandler())); + new HandlerExecutor(mainHandler)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt index 10fc83c8b82c..0016d95dff80 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt @@ -26,7 +26,6 @@ import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager import com.android.systemui.util.kotlin.getOrNull import dagger.BindsInstance -import dagger.Lazy import dagger.Module import dagger.Provides import dagger.Subcomponent @@ -57,7 +56,6 @@ class SysUIUnfoldModule { rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>, @Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>, @UnfoldBg bgProvider: Optional<UnfoldTransitionProgressProvider>, - unfoldLatencyTracker: Lazy<UnfoldLatencyTracker>, factory: SysUIUnfoldComponent.Factory ): Optional<SysUIUnfoldComponent> { val p1 = provider.getOrNull() @@ -67,7 +65,7 @@ class SysUIUnfoldModule { return if (p1 == null || p2 == null || p3 == null || p4 == null) { Optional.empty() } else { - Optional.of(factory.create(p1, p2, p3, p4, unfoldLatencyTracker.get())) + Optional.of(factory.create(p1, p2, p3, p4)) } } } @@ -82,8 +80,7 @@ interface SysUIUnfoldComponent { @BindsInstance p1: UnfoldTransitionProgressProvider, @BindsInstance p2: NaturalRotationUnfoldProgressProvider, @BindsInstance p3: ScopedUnfoldTransitionProgressProvider, - @BindsInstance @UnfoldBg p4: UnfoldTransitionProgressProvider, - @BindsInstance p5: UnfoldLatencyTracker, + @BindsInstance @UnfoldBg p4: UnfoldTransitionProgressProvider ): SysUIUnfoldComponent } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt index 8c66c2f0fab3..33fa9b8e0062 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt @@ -22,7 +22,6 @@ import android.hardware.devicestate.DeviceStateManager import android.os.Trace import android.util.Log import com.android.internal.util.LatencyTracker -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener @@ -42,7 +41,7 @@ import javax.inject.Inject * For now, the focus is on the time the inner display is visible, but in the future, it is easily * possible to monitor the time to go from the inner screen to the outer. */ -@SysUISingleton +@SysUIUnfoldScope class UnfoldLatencyTracker @Inject constructor( diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt index 8bef53c8c4fb..9bd0e324a964 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -177,17 +177,20 @@ class UnfoldTransitionModule { @Module interface Bindings { - @Binds - @IntoMap - @ClassKey(UnfoldTraceLogger::class) - fun bindUnfoldTraceLogger(impl: UnfoldTraceLogger): CoreStartable - @Binds fun bindRepository(impl: UnfoldTransitionRepositoryImpl): UnfoldTransitionRepository @Binds fun bindInteractor(impl: UnfoldTransitionInteractorImpl): UnfoldTransitionInteractor @Binds fun bindFoldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository } + + @Module + interface Startables { + @Binds + @IntoMap + @ClassKey(UnfoldTraceLogger::class) + fun bindUnfoldTraceLogger(impl: UnfoldTraceLogger): CoreStartable + } } const val UNFOLD_STATUS_BAR = "unfold_status_bar" diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 74e133923378..cf76c0d2e696 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -190,7 +190,7 @@ constructor( } } - tracker.addCallback(callback, backgroundDispatcher.asExecutor()) + tracker.addCallback(callback, mainDispatcher.asExecutor()) send(currentSelectionStatus) awaitClose { tracker.removeCallback(callback) } diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt index adae782eeb98..31a8d864de95 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt @@ -26,7 +26,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.withContext +import kotlinx.coroutines.flow.flowOn /** Utility class that could give information about if animation are enabled in the system */ interface AnimationStatusRepository { @@ -45,24 +45,26 @@ constructor( * Emits true if animations are enabled in the system, after subscribing it immediately emits * the current state */ - override fun areAnimationsEnabled(): Flow<Boolean> = conflatedCallbackFlow { - val initialValue = withContext(backgroundDispatcher) { resolver.areAnimationsEnabled() } - trySend(initialValue) + override fun areAnimationsEnabled(): Flow<Boolean> = + conflatedCallbackFlow { + val initialValue = resolver.areAnimationsEnabled() + trySend(initialValue) - val observer = - object : ContentObserver(backgroundHandler) { - override fun onChange(selfChange: Boolean) { - val updatedValue = resolver.areAnimationsEnabled() - trySend(updatedValue) - } - } + val observer = + object : ContentObserver(backgroundHandler) { + override fun onChange(selfChange: Boolean) { + val updatedValue = resolver.areAnimationsEnabled() + trySend(updatedValue) + } + } - resolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), - /* notifyForDescendants= */ false, - observer - ) + resolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), + /* notifyForDescendants= */ false, + observer + ) - awaitClose { resolver.unregisterContentObserver(observer) } - } + awaitClose { resolver.unregisterContentObserver(observer) } + } + .flowOn(backgroundDispatcher) } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt new file mode 100644 index 000000000000..8ecf250e2bbd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.Tracing +import dagger.Module +import dagger.Provides +import javax.inject.Singleton +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi + +/** Providers for various application-wide coroutines-related constructs. */ +@Module +class GlobalCoroutinesModule { + @Provides + @Singleton + @Application + fun applicationScope( + @Main dispatcherContext: CoroutineContext, + ): CoroutineScope = CoroutineScope(dispatcherContext) + + @Provides + @Singleton + @Main + @Deprecated( + "Use @Main CoroutineContext instead", + ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ) + fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate + + @Provides + @Singleton + @Main + fun mainCoroutineContext(@Tracing tracingCoroutineContext: CoroutineContext): CoroutineContext { + return Dispatchers.Main.immediate + tracingCoroutineContext + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Provides + @Tracing + @Singleton + fun tracingCoroutineContext(): CoroutineContext { + return createCoroutineTracingContext() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt index cf6b0d9d1bfe..a13d85bf272c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt @@ -1,57 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.util.kotlin 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.dagger.qualifiers.Tracing -import com.android.app.tracing.coroutines.createCoroutineTracingContext import dagger.Module import dagger.Provides +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.plus -import kotlin.coroutines.CoroutineContext -/** Providers for various coroutines-related constructs. */ +/** Providers for various SystemIU specific coroutines-related constructs. */ @Module -class CoroutinesModule { - @Provides - @SysUISingleton - @Application - fun applicationScope( - @Main dispatcherContext: CoroutineContext, - ): CoroutineScope = CoroutineScope(dispatcherContext) - +class SysUICoroutinesModule { @Provides @SysUISingleton @Background fun bgApplicationScope( - @Application applicationScope: CoroutineScope, - @Background coroutineContext: CoroutineContext, + @Application applicationScope: CoroutineScope, + @Background coroutineContext: CoroutineContext, ): CoroutineScope = applicationScope.plus(coroutineContext) - @Provides - @SysUISingleton - @Main - @Deprecated( - "Use @Main CoroutineContext instead", - ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext") - ) - fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate - - @Provides - @SysUISingleton - @Main - fun mainCoroutineContext(@Tracing tracingCoroutineContext: CoroutineContext): CoroutineContext { - return Dispatchers.Main.immediate + tracingCoroutineContext - } - /** - * Provide a [CoroutineDispatcher] backed by a thread pool containing at most X threads, where - * X is the number of CPU cores available. + * Provide a [CoroutineDispatcher] backed by a thread pool containing at most X threads, where X + * is the number of CPU cores available. * * Because there are multiple threads at play, there is no serialization order guarantee. You * should use a [kotlinx.coroutines.channels.Channel] for serialization if necessary. @@ -67,19 +57,10 @@ class CoroutinesModule { ) fun bgDispatcher(): CoroutineDispatcher = Dispatchers.IO - @Provides @Background @SysUISingleton fun bgCoroutineContext(@Tracing tracingCoroutineContext: CoroutineContext): CoroutineContext { return Dispatchers.IO + tracingCoroutineContext } - - @OptIn(ExperimentalCoroutinesApi::class) - @Provides - @Tracing - @SysUISingleton - fun tracingCoroutineContext(): CoroutineContext { - return createCoroutineTracingContext() - } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt new file mode 100644 index 000000000000..8d5e55a2917e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dagger + +import android.media.AudioManager +import com.android.settingslib.volume.data.repository.AudioRepository +import com.android.settingslib.volume.data.repository.AudioRepositoryImpl +import com.android.settingslib.volume.domain.interactor.AudioModeInteractor +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import dagger.Module +import dagger.Provides +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope + +/** Dagger module for audio code in the volume package */ +@Module +interface AudioModule { + + companion object { + + @Provides + fun provideAudioRepository( + audioManager: AudioManager, + @Background coroutineContext: CoroutineContext, + @Application coroutineScope: CoroutineScope, + ): AudioRepository = AudioRepositoryImpl(audioManager, coroutineContext, coroutineScope) + + @Provides + fun provideAudioModeInteractor(repository: AudioRepository): AudioModeInteractor = + AudioModeInteractor(repository) + } +} 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 b1bfbe0016e1..c842e5f295b9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -60,6 +60,9 @@ import kotlinx.coroutines.CoroutineScope; /** Dagger Module for code in the volume package. */ @Module( + includes = { + AudioModule.class, + }, subcomponents = { VolumePanelComponent.class } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 7c6ad233d853..1e801aeb5a29 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -32,8 +32,6 @@ import android.content.pm.UserInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; -import android.os.HandlerExecutor; -import android.os.HandlerThread; import android.os.IBinder; import android.util.Log; import android.view.Display; @@ -127,7 +125,6 @@ public final class WMShell implements private final DisplayTracker mDisplayTracker; private final NoteTaskInitializer mNoteTaskInitializer; private final Executor mSysUiMainExecutor; - private HandlerThread mHandlerThread; // Listeners and callbacks. Note that we prefer member variable over anonymous class here to // avoid the situation that some implementations, like KeyguardUpdateMonitor, use WeakReference @@ -209,8 +206,6 @@ public final class WMShell implements mDisplayTracker = displayTracker; mNoteTaskInitializer = noteTaskInitializer; mSysUiMainExecutor = sysUiMainExecutor; - mHandlerThread = new HandlerThread("WMShell"); - mHandlerThread.start(); } @Override @@ -224,8 +219,7 @@ public final class WMShell implements mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); // Subscribe to user changes - mUserTracker.addCallback(mUserChangedCallback, - new HandlerExecutor(mHandlerThread.getThreadHandler())); + mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java index 44770fab2d30..b23dfdc68a87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java @@ -16,8 +16,10 @@ package com.android.systemui.accessibility; +import android.annotation.NonNull; import android.graphics.Rect; import android.graphics.Region; +import android.os.IBinder; import android.view.Display; import android.view.View; import android.view.ViewGroup; @@ -89,6 +91,11 @@ public class TestableWindowManager implements WindowManager { return mWindowManager.getMaximumWindowMetrics(); } + @Override + public @NonNull IBinder getDefaultToken() { + return mWindowManager.getDefaultToken(); + } + public View getAttachedView() { return mView; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index d86d12303140..8299acbc2d52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -21,8 +21,10 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -33,10 +35,16 @@ import android.animation.ValueAnimator; import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; +import android.os.Binder; import android.os.Handler; import android.os.RemoteException; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.AndroidTestingRunner; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -46,6 +54,7 @@ import android.view.animation.AccelerateInterpolator; import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.model.SysUiState; @@ -63,7 +72,10 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; @LargeTest @RunWith(AndroidTestingRunner.class) @@ -71,6 +83,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Rule public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); private static final float DEFAULT_SCALE = 4.0f; private static final float DEFAULT_CENTER_X = 400.0f; private static final float DEFAULT_CENTER_Y = 500.0f; @@ -107,6 +121,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { private TestableWindowManager mWindowManager; private ValueAnimator mValueAnimator; + // This list contains all SurfaceControlViewHosts created during a given test. If the + // magnification window is recreated during a test, the list will contain more than a single + // element. + private List<SurfaceControlViewHost> mSurfaceControlViewHosts = new ArrayList<>(); + // The most recently created SurfaceControlViewHost. + private SurfaceControlViewHost mSurfaceControlViewHost; + private SurfaceControl.Transaction mTransaction; @Before public void setUp() throws Exception { @@ -123,10 +144,27 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mValueAnimator = newValueAnimator(); mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( mContext, mValueAnimator); - mController = new SpyWindowMagnificationController(mContext, mHandler, + + Supplier<SurfaceControlViewHost> scvhSupplier = () -> { + mSurfaceControlViewHost = spy(new SurfaceControlViewHost( + mContext, mContext.getDisplay(), new Binder(), "WindowMagnification")); + mSurfaceControlViewHosts.add(mSurfaceControlViewHost); + return mSurfaceControlViewHost; + }; + + mTransaction = spy(new SurfaceControl.Transaction()); + mController = new SpyWindowMagnificationController( + mContext, + mHandler, mWindowMagnificationAnimationController, - mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(), - mWindowMagnifierCallback, mSysUiState, mSecureSettings); + /* mirrorWindowControl= */ null, + mTransaction, + mWindowMagnifierCallback, + mSysUiState, + mSecureSettings, + scvhSupplier, + mSfVsyncFrameProvider); + mSpyController = mController.getSpyController(); } @@ -235,8 +273,52 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); } + @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) @Test - public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback() + public void + enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOn_AnimationAndCallbackTrue() + throws RemoteException { + enableWindowMagnificationWithoutAnimation(); + + // Wait for Rects updated. + waitForIdleSync(); + View mirrorView = mSurfaceControlViewHost.getView(); + final float targetScale = 1.0f; + // Move the magnifier to the top left corner, within the boundary + final float targetCenterX = mirrorView.getWidth() / 2.0f; + final float targetCenterY = mirrorView.getHeight() / 2.0f; + + Mockito.reset(mSpyController); + getInstrumentation().runOnMainSync(() -> { + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, mAnimationCallback); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + advanceTimeBy(mWaitAnimationDuration); + }); + + verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture(), + mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); + verifyStartValue(mScaleCaptor, mCurrentScale.get()); + verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); + verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); + verifyStartValue(mOffsetXCaptor, 0f); + verifyStartValue(mOffsetYCaptor, 0f); + + verifyFinalSpec(targetScale, targetCenterX, targetCenterY); + + verify(mAnimationCallback).onResult(true); + assertEquals(WindowMagnificationAnimationController.STATE_ENABLED, + mWindowMagnificationAnimationController.getState()); + } + + @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) + @Test + public void + enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOff_AnimationAndCallbackTrue() throws RemoteException { enableWindowMagnificationWithoutAnimation(); @@ -475,8 +557,46 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback2).onResult(true); } + @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) + @Test + public void enableWindowMagnificationWithOffset_windowlessFlagOn_expectedValues() { + final float offsetRatio = -0.1f; + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + + Mockito.reset(mSpyController); + getInstrumentation().runOnMainSync(() -> { + mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, + windowBounds.exactCenterX(), windowBounds.exactCenterY(), + offsetRatio, offsetRatio, mAnimationCallback); + advanceTimeBy(mWaitAnimationDuration); + }); + // Wait for Rects update + waitForIdleSync(); + + final int mirrorSurfaceMargin = mContext.getResources().getDimensionPixelSize( + R.dimen.magnification_mirror_surface_margin); + final int defaultMagnificationWindowSize = + mController.getMagnificationWindowSizeFromIndex( + WindowMagnificationSettings.MagnificationSize.MEDIUM); + final int defaultMagnificationFrameSize = + defaultMagnificationWindowSize - 2 * mirrorSurfaceMargin; + final int expectedOffset = (int) (defaultMagnificationFrameSize / 2 * offsetRatio); + + final float expectedX = (int) (windowBounds.exactCenterX() + expectedOffset + - defaultMagnificationWindowSize / 2); + final float expectedY = (int) (windowBounds.exactCenterY() + expectedOffset + - defaultMagnificationWindowSize / 2); + + // This is called 4 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is + // created and we place the mirrored content as a child of the SurfaceView + // (3) the animation starts (4) the animation updates + verify(mTransaction, times(4)) + .setPosition(any(SurfaceControl.class), eq(expectedX), eq(expectedY)); + } + + @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) @Test - public void enableWindowMagnificationWithOffset_expectedValues() { + public void enableWindowMagnificationWithOffset_windowlessFlagOff_expectedValues() { final float offsetRatio = -0.1f; final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); @@ -876,23 +996,28 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { private static class SpyWindowMagnificationController extends WindowMagnificationController { private WindowMagnificationController mSpyController; - SpyWindowMagnificationController(Context context, Handler handler, + SpyWindowMagnificationController(Context context, + Handler handler, WindowMagnificationAnimationController animationController, - SfVsyncFrameCallbackProvider sfVsyncFrameProvider, - MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, - WindowMagnifierCallback callback, SysUiState sysUiState, - SecureSettings secureSettings) { + MirrorWindowControl mirrorWindowControl, + SurfaceControl.Transaction transaction, + WindowMagnifierCallback callback, + SysUiState sysUiState, + SecureSettings secureSettings, + Supplier<SurfaceControlViewHost> scvhSupplier, + SfVsyncFrameCallbackProvider sfVsyncFrameProvider) { super( context, handler, animationController, - sfVsyncFrameProvider, mirrorWindowControl, transaction, callback, sysUiState, - WindowManagerGlobal::getWindowSession, - secureSettings); + secureSettings, + scvhSupplier, + sfVsyncFrameProvider, + WindowManagerGlobal::getWindowSession); mSpyController = Mockito.mock(WindowMagnificationController.class); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 044881e57897..2225ad6e49d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -68,6 +68,9 @@ import android.graphics.RegionIterator; import android.os.Handler; import android.os.RemoteException; import android.os.SystemClock; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -90,8 +93,10 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.AnimatorTestRule; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.model.SysUiState; import com.android.systemui.res.R; import com.android.systemui.settings.FakeDisplayTracker; @@ -120,10 +125,13 @@ import java.util.concurrent.atomic.AtomicInteger; @LargeTest @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) +@RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) public class WindowMagnificationControllerTest extends SysuiTestCase { @Rule public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000; @Mock @@ -159,6 +167,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { private View mSpyView; private View.OnTouchListener mTouchListener; private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); + private KosmosJavaAdapter mKosmos; /** * return whether window magnification is supported for current test context. @@ -170,6 +179,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mKosmos = new KosmosJavaAdapter(this); mContext = Mockito.spy(getContext()); mHandler = new FakeHandler(TestableLooper.get(this).getLooper()); mInstrumentation = InstrumentationRegistry.getInstrumentation(); @@ -185,7 +195,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { return null; }).when(mSfVsyncFrameProvider).postFrameCallback( any(FrameCallback.class)); - mSysUiState = new SysUiState(mDisplayTracker); + mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin()); mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class)); when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then( returnsSecondArg()); @@ -213,13 +223,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mContext, mHandler, mWindowMagnificationAnimationController, - mSfVsyncFrameProvider, mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState, - () -> mWindowSessionSpy, - mSecureSettings); + mSecureSettings, + /* scvhSupplier= */ () -> null, + mSfVsyncFrameProvider, + /* globalWindowSessionSupplier= */ () -> mWindowSessionSpy); verify(mMirrorWindowControl).setWindowDelegate( any(MirrorWindowControl.MirrorWindowDelegate.class)); @@ -267,7 +278,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mInstrumentation.runOnMainSync( () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN, /* magnificationFrameOffsetRatioX= */ 0, - /* magnificationFrameOffsetRatioY= */ 0, null)); + /* magnificationFrameOffsetRatioY= */ 0, null)); // Waits for the surface created verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged( @@ -1412,7 +1423,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { } private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x, - float y) { + float y) { return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y); } @@ -1471,4 +1482,4 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { }); } } -} +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java new file mode 100644 index 000000000000..66fb63b6c331 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java @@ -0,0 +1,1502 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility; + +import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_UP; +import static android.view.WindowInsets.Type.systemGestures; +import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItems; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.AdditionalAnswers.returnsSecondArg; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.animation.ValueAnimator; +import android.annotation.IdRes; +import android.annotation.Nullable; +import android.app.Instrumentation; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Insets; +import android.graphics.PointF; +import android.graphics.Rect; +import android.os.Binder; +import android.os.Handler; +import android.os.RemoteException; +import android.os.SystemClock; +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.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableResources; +import android.text.TextUtils; +import android.util.Size; +import android.view.AttachedSurfaceControl; +import android.view.Display; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewRootImpl; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.IRemoteMagnificationAnimationCallback; +import android.widget.FrameLayout; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; + +import com.android.systemui.Flags; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.AnimatorTestRule; +import com.android.systemui.kosmos.KosmosJavaAdapter; +import com.android.systemui.model.SysUiState; +import com.android.systemui.res.R; +import com.android.systemui.settings.FakeDisplayTracker; +import com.android.systemui.util.leak.ReferenceTestUtils; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.utils.os.FakeHandler; + +import com.google.common.util.concurrent.AtomicDouble; + +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +@LargeTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner.class) +@RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) +public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase { + + @Rule + public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000; + @Mock + private MirrorWindowControl mMirrorWindowControl; + @Mock + private WindowMagnifierCallback mWindowMagnifierCallback; + @Mock + IRemoteMagnificationAnimationCallback mAnimationCallback; + @Mock + IRemoteMagnificationAnimationCallback mAnimationCallback2; + + private SurfaceControl.Transaction mTransaction; + @Mock + private SecureSettings mSecureSettings; + + private long mWaitAnimationDuration; + private long mWaitBounceEffectDuration; + + private Handler mHandler; + private TestableWindowManager mWindowManager; + private SysUiState mSysUiState; + private Resources mResources; + private WindowMagnificationAnimationController mWindowMagnificationAnimationController; + private WindowMagnificationController mWindowMagnificationController; + private Instrumentation mInstrumentation; + private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0); + private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); + + private View mSpyView; + private View.OnTouchListener mTouchListener; + + private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); + + // This list contains all SurfaceControlViewHosts created during a given test. If the + // magnification window is recreated during a test, the list will contain more than a single + // element. + private List<SurfaceControlViewHost> mSurfaceControlViewHosts = new ArrayList<>(); + // The most recently created SurfaceControlViewHost. + private SurfaceControlViewHost mSurfaceControlViewHost; + private KosmosJavaAdapter mKosmos; + + /** + * return whether window magnification is supported for current test context. + */ + private boolean isWindowModeSupported() { + return getContext().getPackageManager().hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mKosmos = new KosmosJavaAdapter(this); + mContext = Mockito.spy(getContext()); + mHandler = new FakeHandler(TestableLooper.get(this).getLooper()); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + final WindowManager wm = mContext.getSystemService(WindowManager.class); + mWindowManager = spy(new TestableWindowManager(wm)); + + mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); + mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin()); + mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class)); + when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then( + returnsSecondArg()); + when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then( + returnsSecondArg()); + + mResources = getContext().getOrCreateTestableResources().getResources(); + // prevent the config orientation from undefined, which may cause config.diff method + // neglecting the orientation update. + if (mResources.getConfiguration().orientation == ORIENTATION_UNDEFINED) { + mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT; + } + + // Using the animation duration in WindowMagnificationAnimationController for testing. + mWaitAnimationDuration = mResources.getInteger( + com.android.internal.R.integer.config_longAnimTime); + // Using the bounce effect duration in WindowMagnificationController for testing. + mWaitBounceEffectDuration = mResources.getInteger( + com.android.internal.R.integer.config_shortAnimTime); + + mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( + mContext, mValueAnimator); + Supplier<SurfaceControlViewHost> scvhSupplier = () -> { + mSurfaceControlViewHost = spy(new SurfaceControlViewHost( + mContext, mContext.getDisplay(), new Binder(), "WindowMagnification")); + ViewRootImpl viewRoot = mock(ViewRootImpl.class); + when(mSurfaceControlViewHost.getRootSurfaceControl()).thenReturn(viewRoot); + mSurfaceControlViewHosts.add(mSurfaceControlViewHost); + return mSurfaceControlViewHost; + }; + mTransaction = spy(new SurfaceControl.Transaction()); + mWindowMagnificationController = + new WindowMagnificationController( + mContext, + mHandler, + mWindowMagnificationAnimationController, + mMirrorWindowControl, + mTransaction, + mWindowMagnifierCallback, + mSysUiState, + mSecureSettings, + scvhSupplier, + /* sfVsyncFrameProvider= */ null, + /* globalWindowSessionSupplier= */ null); + + verify(mMirrorWindowControl).setWindowDelegate( + any(MirrorWindowControl.MirrorWindowDelegate.class)); + mSpyView = Mockito.spy(new View(mContext)); + doAnswer((invocation) -> { + mTouchListener = invocation.getArgument(0); + return null; + }).when(mSpyView).setOnTouchListener( + any(View.OnTouchListener.class)); + + // skip test if window magnification is not supported to prevent fail results. (b/279820875) + Assume.assumeTrue(isWindowModeSupported()); + } + + @After + public void tearDown() { + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.deleteWindowMagnification()); + mValueAnimator.cancel(); + } + + @Test + public void initWindowMagnificationController_checkAllowDiagonalScrollingWithSecureSettings() { + verify(mSecureSettings).getIntForUser( + eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING), + /* def */ eq(1), /* userHandle= */ anyInt()); + assertTrue(mWindowMagnificationController.isDiagonalScrollingEnabled()); + } + + @Test + public void enableWindowMagnification_showControlAndNotifyBoundsChanged() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + verify(mMirrorWindowControl).showControl(); + verify(mWindowMagnifierCallback, + timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeastOnce()).onWindowMagnifierBoundsChanged( + eq(mContext.getDisplayId()), any(Rect.class)); + } + + @Test + public void enableWindowMagnification_notifySourceBoundsChanged() { + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, /* magnificationFrameOffsetRatioX= */ 0, + /* magnificationFrameOffsetRatioY= */ 0, null)); + + // Waits for the surface created + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged( + (eq(mContext.getDisplayId())), any()); + } + + @Test + public void enableWindowMagnification_disabled_notifySourceBoundsChanged() { + enableWindowMagnification_notifySourceBoundsChanged(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.deleteWindowMagnification(null)); + Mockito.reset(mWindowMagnifierCallback); + + enableWindowMagnification_notifySourceBoundsChanged(); + } + + @Test + public void enableWindowMagnification_withAnimation_schedulesFrame() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(2.0f, 10, + 10, /* magnificationFrameOffsetRatioX= */ 0, + /* magnificationFrameOffsetRatioY= */ 0, + Mockito.mock(IRemoteMagnificationAnimationCallback.class)); + }); + advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS); + + verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(), + eq(Surface.ROTATION_0)); + } + + @Test + public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, 0, 0, null); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifier(10, 10); + }); + + final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class); + verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged( + (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + assertEquals(mWindowMagnificationController.getCenterX(), + sourceBoundsCaptor.getValue().exactCenterX(), 0); + assertEquals(mWindowMagnificationController.getCenterY(), + sourceBoundsCaptor.getValue().exactCenterY(), 0); + } + + @Test + public void enableWindowMagnification_systemGestureExclusionRectsIsSet() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + // Wait for Rects updated. + waitForIdleSync(); + + List<Rect> rects = mSurfaceControlViewHost.getView().getSystemGestureExclusionRects(); + assertFalse(rects.isEmpty()); + } + + @Ignore("The default window size should be constrained after fixing b/288056772") + @Test + public void enableWindowMagnification_LargeScreen_windowSizeIsConstrained() { + final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10; + mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize)); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + final int halfScreenSize = screenSize / 2; + ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams(); + // The frame size should be the half of smaller value of window height/width unless it + //exceed the max frame size. + assertTrue(params.width < halfScreenSize); + assertTrue(params.height < halfScreenSize); + } + + @Test + public void deleteWindowMagnification_destroyControlAndUnregisterComponentCallback() { + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, + Float.NaN)); + + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.deleteWindowMagnification()); + + verify(mMirrorWindowControl).destroyControl(); + verify(mContext).unregisterComponentCallbacks(mWindowMagnificationController); + } + + @Test + public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() { + final WindowManager wm = mContext.getSystemService(WindowManager.class); + final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); + setSystemGestureInsets(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + bounds.bottom); + }); + ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.deleteWindowMagnification(); + }); + + verify(mMirrorWindowControl).destroyControl(); + assertFalse(hasMagnificationOverlapFlag()); + } + + @Test + public void deleteWindowMagnification_notifySourceBoundsChanged() { + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, + Float.NaN)); + + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.deleteWindowMagnification()); + + // The first time is for notifying magnification enabled and the second time is for + // notifying magnification disabled. + verify(mWindowMagnifierCallback, times(2)).onSourceBoundsChanged( + (eq(mContext.getDisplayId())), any()); + } + + @Test + public void moveMagnifier_schedulesFrame() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + waitForIdleSync(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.moveWindowMagnifier(100f, 100f)); + + verify(mTransaction, atLeastOnce()).setGeometry(any(), any(), any(), + eq(Surface.ROTATION_0)); + } + + @Test + public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback() + throws RemoteException { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, 0, 0, null); + }); + + final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10; + final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10; + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifierToPosition( + targetCenterX, targetCenterY, mAnimationCallback); + }); + advanceTimeBy(mWaitAnimationDuration); + + verify(mAnimationCallback, times(1)).onResult(eq(true)); + verify(mAnimationCallback, never()).onResult(eq(false)); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + assertEquals(mWindowMagnificationController.getCenterX(), + sourceBoundsCaptor.getValue().exactCenterX(), 0); + assertEquals(mWindowMagnificationController.getCenterY(), + sourceBoundsCaptor.getValue().exactCenterY(), 0); + assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0); + assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0); + } + + @Test + public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback() + throws RemoteException { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN, 0, 0, null); + }); + + final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + final float centerX = sourceBoundsCaptor.getValue().exactCenterX(); + final float centerY = sourceBoundsCaptor.getValue().exactCenterY(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 10, centerY + 10, mAnimationCallback); + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 20, centerY + 20, mAnimationCallback); + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 30, centerY + 30, mAnimationCallback); + mWindowMagnificationController.moveWindowMagnifierToPosition( + centerX + 40, centerY + 40, mAnimationCallback2); + }); + advanceTimeBy(mWaitAnimationDuration); + + // only the last one callback will return true + verify(mAnimationCallback2).onResult(eq(true)); + // the others will return false + verify(mAnimationCallback, times(3)).onResult(eq(false)); + verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)) + .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture()); + assertEquals(mWindowMagnificationController.getCenterX(), + sourceBoundsCaptor.getValue().exactCenterX(), 0); + assertEquals(mWindowMagnificationController.getCenterY(), + sourceBoundsCaptor.getValue().exactCenterY(), 0); + assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0); + assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0); + } + + @Test + public void setScale_enabled_expectedValueAndUpdateStateDescription() { + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f, + Float.NaN, Float.NaN)); + + mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f)); + + assertEquals(3.0f, mWindowMagnificationController.getScale(), 0); + final View mirrorView = mSurfaceControlViewHost.getView(); + assertNotNull(mirrorView); + assertThat(mirrorView.getStateDescription().toString(), containsString("300")); + } + + @Test + public void onConfigurationChanged_disabled_withoutException() { + Display display = Mockito.spy(mContext.getDisplay()); + when(display.getRotation()).thenReturn(Surface.ROTATION_90); + when(mContext.getDisplay()).thenReturn(display); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE); + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); + }); + } + + @Test + public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() { + final int newRotation = simulateRotateTheDevice(); + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY()); + final float displayWidth = windowBounds.width(); + final PointF magnifiedCenter = new PointF(center, center + 5f); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + magnifiedCenter.x, magnifiedCenter.y); + // Get the center again in case the center we set is out of screen. + magnifiedCenter.set(mWindowMagnificationController.getCenterX(), + mWindowMagnificationController.getCenterY()); + }); + // Rotate the window clockwise 90 degree. + windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom, + windowBounds.right); + mWindowManager.setWindowBounds(windowBounds); + + mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged( + ActivityInfo.CONFIG_ORIENTATION)); + + assertEquals(newRotation, mWindowMagnificationController.mRotation); + final PointF expectedCenter = new PointF(magnifiedCenter.y, + displayWidth - magnifiedCenter.x); + final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(), + mWindowMagnificationController.getCenterY()); + assertEquals(expectedCenter, actualCenter); + } + + @Test + public void onOrientationChanged_disabled_updateDisplayRotation() { + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + // Rotate the window clockwise 90 degree. + windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom, + windowBounds.right); + mWindowManager.setWindowBounds(windowBounds); + final int newRotation = simulateRotateTheDevice(); + + mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged( + ActivityInfo.CONFIG_ORIENTATION)); + + assertEquals(newRotation, mWindowMagnificationController.mRotation); + } + + @Test + public void onScreenSizeAndDensityChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() { + // The default position is at the center of the screen. + final float expectedRatio = 0.5f; + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + // Screen size and density change + mContext.getResources().getConfiguration().smallestScreenWidthDp = + mContext.getResources().getConfiguration().smallestScreenWidthDp * 2; + final Rect testWindowBounds = new Rect( + mWindowManager.getCurrentWindowMetrics().getBounds()); + testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, + testWindowBounds.right + 100, testWindowBounds.bottom + 100); + mWindowManager.setWindowBounds(testWindowBounds); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); + }); + + // The ratio of center to window size should be the same. + assertEquals(expectedRatio, + mWindowMagnificationController.getCenterX() / testWindowBounds.width(), + 0); + assertEquals(expectedRatio, + mWindowMagnificationController.getCenterY() / testWindowBounds.height(), + 0); + } + + @Test + public void onScreenChangedToSavedDensity_enabled_restoreSavedMagnifierWindow() { + mContext.getResources().getConfiguration().smallestScreenWidthDp = + mContext.getResources().getConfiguration().smallestScreenWidthDp * 2; + int windowFrameSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + mWindowMagnificationController.mWindowMagnificationSizePrefs.saveSizeForCurrentDensity( + new Size(windowFrameSize, windowFrameSize)); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams(); + assertTrue(params.width == windowFrameSize); + assertTrue(params.height == windowFrameSize); + } + + @Test + public void screenSizeIsChangedToLarge_enabled_defaultWindowSize() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10; + // Screen size and density change + mContext.getResources().getConfiguration().smallestScreenWidthDp = + mContext.getResources().getConfiguration().smallestScreenWidthDp * 2; + mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize)); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); + }); + + final int defaultWindowSize = + mWindowMagnificationController.getMagnificationWindowSizeFromIndex( + WindowMagnificationSettings.MagnificationSize.MEDIUM); + ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams(); + + assertTrue(params.width == defaultWindowSize); + assertTrue(params.height == defaultWindowSize); + } + + @Test + public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + Mockito.reset(mWindowManager); + Mockito.reset(mMirrorWindowControl); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); + }); + + verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt()); + verify(mSurfaceControlViewHosts.get(0)).release(); + verify(mMirrorWindowControl).destroyControl(); + verify(mSurfaceControlViewHosts.get(1)).setView(any(), any()); + verify(mMirrorWindowControl).showControl(); + } + + @Test + public void onDensityChanged_disabled_updateDimensions() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); + }); + + verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt()); + } + + @Test + public void initializeA11yNode_enabled_expectedValues() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN, + Float.NaN); + }); + final View mirrorView = mSurfaceControlViewHost.getView(); + assertNotNull(mirrorView); + final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo(); + + mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo); + + assertNotNull(nodeInfo.getContentDescription()); + assertThat(nodeInfo.getStateDescription().toString(), containsString("250")); + assertThat(nodeInfo.getActionList(), + hasItems(new AccessibilityAction(R.id.accessibility_action_zoom_in, null), + new AccessibilityAction(R.id.accessibility_action_zoom_out, null), + new AccessibilityAction(R.id.accessibility_action_move_right, null), + new AccessibilityAction(R.id.accessibility_action_move_left, null), + new AccessibilityAction(R.id.accessibility_action_move_down, null), + new AccessibilityAction(R.id.accessibility_action_move_up, null))); + } + + @Test + public void performA11yActions_visible_expectedResults() { + final int displayId = mContext.getDisplayId(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(1.5f, Float.NaN, + Float.NaN); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + assertTrue( + mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null)); + // Minimum scale is 1.0. + verify(mWindowMagnifierCallback).onPerformScaleAction( + eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true)); + + assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null)); + verify(mWindowMagnifierCallback).onPerformScaleAction( + eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true)); + + // TODO: Verify the final state when the mirror surface is visible. + assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null)); + assertTrue( + mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null)); + assertTrue( + mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null)); + assertTrue( + mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null)); + verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId)); + + assertTrue(mirrorView.performAccessibilityAction( + AccessibilityAction.ACTION_CLICK.getId(), null)); + verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId)); + } + + @Test + public void performA11yActions_visible_notifyAccessibilityActionPerformed() { + final int displayId = mContext.getDisplayId(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN, + Float.NaN); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null); + + verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId)); + } + + @Test + public void windowMagnifierEditMode_performA11yClickAction_exitEditMode() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + View closeButton = getInternalView(R.id.close_button); + View bottomRightCorner = getInternalView(R.id.bottom_right_corner); + View bottomLeftCorner = getInternalView(R.id.bottom_left_corner); + View topRightCorner = getInternalView(R.id.top_right_corner); + View topLeftCorner = getInternalView(R.id.top_left_corner); + + assertEquals(View.VISIBLE, closeButton.getVisibility()); + assertEquals(View.VISIBLE, bottomRightCorner.getVisibility()); + assertEquals(View.VISIBLE, bottomLeftCorner.getVisibility()); + assertEquals(View.VISIBLE, topRightCorner.getVisibility()); + assertEquals(View.VISIBLE, topLeftCorner.getVisibility()); + + final View mirrorView = mSurfaceControlViewHost.getView(); + mInstrumentation.runOnMainSync(() -> + mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(), + null)); + + assertEquals(View.GONE, closeButton.getVisibility()); + assertEquals(View.GONE, bottomRightCorner.getVisibility()); + assertEquals(View.GONE, bottomLeftCorner.getVisibility()); + assertEquals(View.GONE, topRightCorner.getVisibility()); + assertEquals(View.GONE, topLeftCorner.getVisibility()); + } + + @Test + + public void windowWidthIsNotMax_performA11yActionIncreaseWidth_windowWidthIncreased() { + final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + final int startingWidth = (int) (windowBounds.width() * 0.8); + final int startingHeight = (int) (windowBounds.height() * 0.8); + final float changeWindowSizeAmount = mContext.getResources().getFraction( + R.fraction.magnification_resize_window_size_amount, + /* base= */ 1, + /* pbase= */ 1); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingWidth, startingHeight); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + + mInstrumentation.runOnMainSync( + () -> { + mirrorView.performAccessibilityAction( + R.id.accessibility_action_increase_window_width, null); + actualWindowHeight.set( + mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set( + mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + final int mirrorSurfaceMargin = mResources.getDimensionPixelSize( + R.dimen.magnification_mirror_surface_margin); + // Window width includes the magnifier frame and the margin. Increasing the window size + // will be increasing the amount of the frame size only. + int newWindowWidth = + (int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount)) + + 2 * mirrorSurfaceMargin; + assertEquals(newWindowWidth, actualWindowWidth.get()); + assertEquals(startingHeight, actualWindowHeight.get()); + } + + @Test + public void windowHeightIsNotMax_performA11yActionIncreaseHeight_windowHeightIncreased() { + final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + final int startingWidth = (int) (windowBounds.width() * 0.8); + final int startingHeight = (int) (windowBounds.height() * 0.8); + final float changeWindowSizeAmount = mContext.getResources().getFraction( + R.fraction.magnification_resize_window_size_amount, + /* base= */ 1, + /* pbase= */ 1); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingWidth, startingHeight); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + + mInstrumentation.runOnMainSync( + () -> { + mirrorView.performAccessibilityAction( + R.id.accessibility_action_increase_window_height, null); + actualWindowHeight.set( + mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set( + mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + final int mirrorSurfaceMargin = mResources.getDimensionPixelSize( + R.dimen.magnification_mirror_surface_margin); + // Window height includes the magnifier frame and the margin. Increasing the window size + // will be increasing the amount of the frame size only. + int newWindowHeight = + (int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount)) + + 2 * mirrorSurfaceMargin; + assertEquals(startingWidth, actualWindowWidth.get()); + assertEquals(newWindowHeight, actualWindowHeight.get()); + } + + @Test + public void windowWidthIsMax_noIncreaseWindowWidthA11yAction() { + final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + final int startingWidth = windowBounds.width(); + final int startingHeight = windowBounds.height(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingWidth, startingHeight); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AccessibilityNodeInfo accessibilityNodeInfo = + mirrorView.createAccessibilityNodeInfo(); + assertFalse(accessibilityNodeInfo.getActionList().contains( + new AccessibilityAction(R.id.accessibility_action_increase_window_width, null))); + } + + @Test + public void windowHeightIsMax_noIncreaseWindowHeightA11yAction() { + final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + final int startingWidth = windowBounds.width(); + final int startingHeight = windowBounds.height(); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingWidth, startingHeight); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AccessibilityNodeInfo accessibilityNodeInfo = + mirrorView.createAccessibilityNodeInfo(); + assertFalse(accessibilityNodeInfo.getActionList().contains( + new AccessibilityAction(R.id.accessibility_action_increase_window_height, null))); + } + + @Test + public void windowWidthIsNotMin_performA11yActionDecreaseWidth_windowWidthDecreased() { + int mMinWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int startingSize = (int) (mMinWindowSize * 1.1); + final float changeWindowSizeAmount = mContext.getResources().getFraction( + R.fraction.magnification_resize_window_size_amount, + /* base= */ 1, + /* pbase= */ 1); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingSize, startingSize); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + + mInstrumentation.runOnMainSync( + () -> { + mirrorView.performAccessibilityAction( + R.id.accessibility_action_decrease_window_width, null); + actualWindowHeight.set( + mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set( + mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + final int mirrorSurfaceMargin = mResources.getDimensionPixelSize( + R.dimen.magnification_mirror_surface_margin); + // Window width includes the magnifier frame and the margin. Decreasing the window size + // will be decreasing the amount of the frame size only. + int newWindowWidth = + (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount)) + + 2 * mirrorSurfaceMargin; + assertEquals(newWindowWidth, actualWindowWidth.get()); + assertEquals(startingSize, actualWindowHeight.get()); + } + + @Test + public void windowHeightIsNotMin_performA11yActionDecreaseHeight_windowHeightDecreased() { + int mMinWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int startingSize = (int) (mMinWindowSize * 1.1); + final float changeWindowSizeAmount = mContext.getResources().getFraction( + R.fraction.magnification_resize_window_size_amount, + /* base= */ 1, + /* pbase= */ 1); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingSize, startingSize); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + + mInstrumentation.runOnMainSync( + () -> { + mirrorView.performAccessibilityAction( + R.id.accessibility_action_decrease_window_height, null); + actualWindowHeight.set( + mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set( + mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + final int mirrorSurfaceMargin = mResources.getDimensionPixelSize( + R.dimen.magnification_mirror_surface_margin); + // Window height includes the magnifier frame and the margin. Decreasing the window size + // will be decreasing the amount of the frame size only. + int newWindowHeight = + (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount)) + + 2 * mirrorSurfaceMargin; + assertEquals(startingSize, actualWindowWidth.get()); + assertEquals(newWindowHeight, actualWindowHeight.get()); + } + + @Test + public void windowWidthIsMin_noDecreaseWindowWidthA11yAction() { + int mMinWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int startingSize = mMinWindowSize; + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingSize, startingSize); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AccessibilityNodeInfo accessibilityNodeInfo = + mirrorView.createAccessibilityNodeInfo(); + assertFalse(accessibilityNodeInfo.getActionList().contains( + new AccessibilityAction(R.id.accessibility_action_decrease_window_width, null))); + } + + @Test + public void windowHeightIsMin_noDecreaseWindowHeightA11yAction() { + int mMinWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int startingSize = mMinWindowSize; + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.setWindowSize(startingSize, startingSize); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + final AccessibilityNodeInfo accessibilityNodeInfo = + mirrorView.createAccessibilityNodeInfo(); + assertFalse(accessibilityNodeInfo.getActionList().contains( + new AccessibilityAction(R.id.accessibility_action_decrease_window_height, null))); + } + + @Test + public void enableWindowMagnification_hasA11yWindowTitle() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + assertEquals(getContext().getResources().getString( + com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle()); + } + + @Test + public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(0.9f, Float.NaN, + Float.NaN); + }); + + assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0); + } + + @Test + public void enableWindowMagnification_rotationIsChanged_updateRotationValue() { + // the config orientation should not be undefined, since it would cause config.diff + // returning 0 and thus the orientation changed would not be detected + assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation); + + final Configuration config = mResources.getConfiguration(); + config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT + : ORIENTATION_LANDSCAPE; + final int newRotation = simulateRotateTheDevice(); + + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + assertEquals(newRotation, mWindowMagnificationController.mRotation); + } + + @Test + public void enableWindowMagnification_registerComponentCallback() { + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, + Float.NaN)); + + verify(mContext).registerComponentCallbacks(mWindowMagnificationController); + } + + @Test + public void onLocaleChanged_enabled_updateA11yWindowTitle() { + final String newA11yWindowTitle = "new a11y window title"; + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + final TestableResources testableResources = getContext().getOrCreateTestableResources(); + testableResources.addOverride(com.android.internal.R.string.android_system_label, + newA11yWindowTitle); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE); + }); + + assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle())); + } + + @Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925") + @Test + public void onSingleTap_enabled_scaleAnimates() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onSingleTap(mSpyView); + }); + + final View mirrorView = mSurfaceControlViewHost.getView(); + + final AtomicDouble maxScaleX = new AtomicDouble(); + advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> { + // For some reason the fancy way doesn't compile... + // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max); + final double oldMax = maxScaleX.get(); + final double newMax = Math.max(mirrorView.getScaleX(), oldMax); + assertTrue(maxScaleX.compareAndSet(oldMax, newMax)); + }); + + assertTrue(maxScaleX.get() > 1.0); + } + + @Test + public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + setSystemGestureInsets(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, + Float.NaN); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.moveWindowMagnifier(0, bounds.height()); + }); + + ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag()); + } + + @Test + public void moveWindowMagnificationToRightEdge_dragHandleMovesToLeftAndUpdatesTapExcludeRegion() + throws RemoteException { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + setSystemGestureInsets(); + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.enableWindowMagnificationInternal( + Float.NaN, Float.NaN, Float.NaN); + }); + // Wait for Region updated. + waitForIdleSync(); + + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.moveWindowMagnifier(bounds.width(), 0); + }); + // Wait for Region updated. + waitForIdleSync(); + + AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl(); + // Verifying two times in: (1) enable window magnification (2) reposition drag handle + verify(viewRoot, times(2)).setTouchableRegion(any()); + + View dragButton = getInternalView(R.id.drag_handle); + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams(); + assertEquals(Gravity.BOTTOM | Gravity.LEFT, params.gravity); + } + + @Test + public void moveWindowMagnificationToLeftEdge_dragHandleMovesToRightAndUpdatesTapExcludeRegion() + throws RemoteException { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + setSystemGestureInsets(); + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.enableWindowMagnificationInternal( + Float.NaN, Float.NaN, Float.NaN); + }); + // Wait for Region updated. + waitForIdleSync(); + + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.moveWindowMagnifier(-bounds.width(), 0); + }); + // Wait for Region updated. + waitForIdleSync(); + + AttachedSurfaceControl viewRoot = mSurfaceControlViewHost.getRootSurfaceControl(); + // Verifying one times in: (1) enable window magnification + verify(viewRoot).setTouchableRegion(any()); + + View dragButton = getInternalView(R.id.drag_handle); + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) dragButton.getLayoutParams(); + assertEquals(Gravity.BOTTOM | Gravity.RIGHT, params.gravity); + } + + @Test + public void setMinimumWindowSize_enabled_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width); + + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setMinimumWindowSizeThenEnable_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN); + actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLessThanMin_enabled_minimumWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(minimumWindowSize - 10, + minimumWindowSize - 10); + actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + assertEquals(minimumWindowSize, actualWindowHeight.get()); + assertEquals(minimumWindowSize, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10); + actualWindowHeight.set(mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set(mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + assertEquals(bounds.height(), actualWindowHeight.get()); + assertEquals(bounds.width(), actualWindowWidth.get()); + } + + @Test + public void changeMagnificationSize_expectedWindowSize() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + + final float magnificationScaleLarge = 2.5f; + final int initSize = Math.min(bounds.width(), bounds.height()) / 3; + final int magnificationSize = (int) (initSize * magnificationScaleLarge) + - (int) (initSize * magnificationScaleLarge) % 2; + + final int expectedWindowHeight = magnificationSize; + final int expectedWindowWidth = magnificationSize; + + mInstrumentation.runOnMainSync( + () -> + mWindowMagnificationController.enableWindowMagnificationInternal( + Float.NaN, Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.changeMagnificationSize( + WindowMagnificationSettings.MagnificationSize.LARGE); + actualWindowHeight.set( + mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set( + mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void editModeOnDragCorner_resizesWindow() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + + final int startingSize = (int) (bounds.width() / 2); + + mInstrumentation.runOnMainSync( + () -> + mWindowMagnificationController.enableWindowMagnificationInternal( + Float.NaN, Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.setWindowSize(startingSize, startingSize); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + }); + + waitForIdleSync(); + + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController + .onDrag(getInternalView(R.id.bottom_right_corner), 2f, 1f); + actualWindowHeight.set( + mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set( + mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + + assertEquals(startingSize + 1, actualWindowHeight.get()); + assertEquals(startingSize + 2, actualWindowWidth.get()); + } + + @Test + public void editModeOnDragEdge_resizesWindowInOnlyOneDirection() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + + final int startingSize = (int) (bounds.width() / 2f); + + mInstrumentation.runOnMainSync( + () -> + mWindowMagnificationController.enableWindowMagnificationInternal( + Float.NaN, Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.setWindowSize(startingSize, startingSize); + mWindowMagnificationController.setEditMagnifierSizeMode(true); + mWindowMagnificationController + .onDrag(getInternalView(R.id.bottom_handle), 2f, 1f); + actualWindowHeight.set( + mSurfaceControlViewHost.getView().getLayoutParams().height); + actualWindowWidth.set( + mSurfaceControlViewHost.getView().getLayoutParams().width); + }); + assertEquals(startingSize + 1, actualWindowHeight.get()); + assertEquals(startingSize, actualWindowWidth.get()); + } + + @Test + public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() { + + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger magnificationCenterX = new AtomicInteger(); + final AtomicInteger magnificationCenterY = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize, + minimumWindowSize, bounds.right, bounds.bottom); + magnificationCenterX.set((int) mWindowMagnificationController.getCenterX()); + magnificationCenterY.set((int) mWindowMagnificationController.getCenterY()); + }); + + assertTrue(magnificationCenterX.get() < bounds.right); + assertTrue(magnificationCenterY.get() < bounds.bottom); + } + + @Test + public void performSingleTap_DragHandle() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationController.enableWindowMagnificationInternal( + 1.5f, bounds.centerX(), bounds.centerY()); + }); + View dragButton = getInternalView(R.id.drag_handle); + + // Perform a single-tap + final long downTime = SystemClock.uptimeMillis(); + dragButton.dispatchTouchEvent( + obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100)); + dragButton.dispatchTouchEvent( + obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100)); + + verify(mSurfaceControlViewHost).setView(any(View.class), any()); + } + + private <T extends View> T getInternalView(@IdRes int idRes) { + View mirrorView = mSurfaceControlViewHost.getView(); + T view = mirrorView.findViewById(idRes); + assertNotNull(view); + return view; + } + + private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x, + float y) { + return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y); + } + + private CharSequence getAccessibilityWindowTitle() { + final View mirrorView = mSurfaceControlViewHost.getView(); + if (mirrorView == null) { + return null; + } + WindowManager.LayoutParams layoutParams = + (WindowManager.LayoutParams) mirrorView.getLayoutParams(); + return layoutParams.accessibilityTitle; + } + + private boolean hasMagnificationOverlapFlag() { + return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0; + } + + private void setSystemGestureInsets() { + final WindowInsets testInsets = new WindowInsets.Builder() + .setInsets(systemGestures(), Insets.of(0, 0, 0, 10)) + .build(); + mWindowManager.setWindowInsets(testInsets); + } + + private int updateMirrorSurfaceMarginDimension() { + return mContext.getResources().getDimensionPixelSize( + R.dimen.magnification_mirror_surface_margin); + } + + @Surface.Rotation + private int simulateRotateTheDevice() { + final Display display = Mockito.spy(mContext.getDisplay()); + final int currentRotation = display.getRotation(); + final int newRotation = (currentRotation + 1) % 4; + when(display.getRotation()).thenReturn(newRotation); + when(mContext.getDisplay()).thenReturn(display); + return newRotation; + } + + // advance time based on the device frame refresh rate + private void advanceTimeBy(long timeDelta) { + advanceTimeBy(timeDelta, /* runnableOnEachRefresh= */ null); + } + + // advance time based on the device frame refresh rate, and trigger runnable on each refresh + private void advanceTimeBy(long timeDelta, @Nullable Runnable runnableOnEachRefresh) { + final float frameRate = mContext.getDisplay().getRefreshRate(); + final int timeSlot = (int) (1000 / frameRate); + int round = (int) Math.ceil((double) timeDelta / timeSlot); + for (; round >= 0; round--) { + mInstrumentation.runOnMainSync(() -> { + mAnimatorTestRule.advanceTimeBy(timeSlot); + if (runnableOnEachRefresh != null) { + runnableOnEachRefresh.run(); + } + }); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index 4a1bdbcc9b48..ca3eb3e077d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -69,6 +69,7 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.test.filters.SmallTest; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.messages.nano.SystemMessageProto; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; @@ -237,7 +238,7 @@ public class MenuViewLayerTest extends SysuiTestCase { final List<String> stubShortcutTargets = new ArrayList<>(); stubShortcutTargets.add(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString()); when(mStubAccessibilityManager.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY)).thenReturn(stubShortcutTargets); + ShortcutConstants.UserShortcutType.HARDWARE)).thenReturn(stubShortcutTargets); mMenuViewLayer.mDismissMenuAction.run(); final String value = Settings.Secure.getString(mSpyContext.getContentResolver(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt new file mode 100644 index 000000000000..ed80a869dda8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt @@ -0,0 +1,98 @@ +package com.android.systemui.keyboard.stickykeys.data.repository + +import android.content.pm.UserInfo +import android.hardware.input.InputManager +import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.keyboard.stickykeys.StickyKeysLogger +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class StickyKeysRepositoryImplTest : SysuiTestCase() { + + private val dispatcher = StandardTestDispatcher() + private val testScope = TestScope(dispatcher) + private val secureSettings = FakeSettings() + private val userRepository = Kosmos().fakeUserRepository + private lateinit var stickyKeysRepository: StickyKeysRepositoryImpl + + @Before + fun setup() { + stickyKeysRepository = StickyKeysRepositoryImpl( + mock<InputManager>(), + dispatcher, + secureSettings, + userRepository, + mock<StickyKeysLogger>() + ) + userRepository.setUserInfos(USER_INFOS) + setStickyKeySettingForUser(enabled = true, userInfo = SETTING_ENABLED_USER) + setStickyKeySettingForUser(enabled = false, userInfo = SETTING_DISABLED_USER) + } + + @Test + fun settingEnabledEmitsValueForCurrentUser() { + testScope.runTest { + userRepository.setSelectedUserInfo(SETTING_ENABLED_USER) + + val enabled by collectLastValue(stickyKeysRepository.settingEnabled) + + assertThat(enabled).isTrue() + } + } + + @Test + fun settingEnabledEmitsNewValueWhenSettingChanges() { + testScope.runTest { + userRepository.setSelectedUserInfo(SETTING_ENABLED_USER) + val enabled by collectValues(stickyKeysRepository.settingEnabled) + runCurrent() + + setStickyKeySettingForUser(enabled = false, userInfo = SETTING_ENABLED_USER) + + assertThat(enabled).containsExactly(true, false).inOrder() + } + } + + @Test + fun settingEnabledEmitsValueForNewUserWhenUserChanges() { + testScope.runTest { + userRepository.setSelectedUserInfo(SETTING_ENABLED_USER) + val enabled by collectLastValue(stickyKeysRepository.settingEnabled) + runCurrent() + + userRepository.setSelectedUserInfo(SETTING_DISABLED_USER) + + assertThat(enabled).isFalse() + } + } + + private fun setStickyKeySettingForUser(enabled: Boolean, userInfo: UserInfo) { + val newValue = if (enabled) "1" else "0" + secureSettings.putStringForUser(ACCESSIBILITY_STICKY_KEYS, newValue, userInfo.id) + } + + private companion object { + val SETTING_ENABLED_USER = UserInfo(/* id= */ 0, "user1", /* flags= */ 0) + val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0) + val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt index 8a713688acf9..6eebb6d19e5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyboard.stickykeys.ui.viewmodel import android.hardware.input.InputManager import android.hardware.input.StickyModifierState +import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -31,8 +32,11 @@ import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT_GR import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock +import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher @@ -57,6 +61,8 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { private lateinit var viewModel: StickyKeysIndicatorViewModel private val inputManager = mock<InputManager>() private val keyboardRepository = FakeKeyboardRepository() + private val secureSettings = FakeSettings() + private val userRepository = Kosmos().fakeUserRepository private val captor = ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java) @@ -65,8 +71,11 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { val stickyKeysRepository = StickyKeysRepositoryImpl( inputManager, dispatcher, + secureSettings, + userRepository, mock<StickyKeysLogger>() ) + setStickyKeySetting(enabled = false) viewModel = StickyKeysIndicatorViewModel( stickyKeysRepository = stickyKeysRepository, @@ -76,13 +85,24 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { } @Test - fun startsListeningToStickyKeysOnlyWhenKeyboardIsConnected() { + fun doesntListenToStickyKeysOnlyWhenKeyboardIsConnected() { testScope.runTest { collectLastValue(viewModel.indicatorContent) + + keyboardRepository.setIsAnyKeyboardConnected(true) runCurrent() + verifyZeroInteractions(inputManager) + } + } + @Test + fun startsListeningToStickyKeysOnlyWhenKeyboardIsConnectedAndSettingIsOn() { + testScope.runTest { + collectLastValue(viewModel.indicatorContent) keyboardRepository.setIsAnyKeyboardConnected(true) + + setStickyKeySetting(enabled = true) runCurrent() verify(inputManager) @@ -93,11 +113,31 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { } } + private fun setStickyKeySetting(enabled: Boolean) { + val newValue = if (enabled) "1" else "0" + val defaultUser = userRepository.getSelectedUserInfo().id + secureSettings.putStringForUser(ACCESSIBILITY_STICKY_KEYS, newValue, defaultUser) + } + + @Test + fun stopsListeningToStickyKeysWhenStickyKeySettingsIsTurnedOff() { + testScope.runTest { + collectLastValue(viewModel.indicatorContent) + setStickyKeysActive() + runCurrent() + + setStickyKeySetting(enabled = false) + runCurrent() + + verify(inputManager).unregisterStickyModifierStateListener(any()) + } + } + @Test fun stopsListeningToStickyKeysWhenKeyboardDisconnects() { testScope.runTest { collectLastValue(viewModel.indicatorContent) - keyboardRepository.setIsAnyKeyboardConnected(true) + setStickyKeysActive() runCurrent() keyboardRepository.setIsAnyKeyboardConnected(false) @@ -111,7 +151,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { fun emitsStickyKeysListWhenStickyKeyIsPressed() { testScope.runTest { val stickyKeys by collectLastValue(viewModel.indicatorContent) - keyboardRepository.setIsAnyKeyboardConnected(true) + setStickyKeysActive() setStickyKeys(mapOf(ALT to false)) @@ -123,7 +163,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { fun emitsEmptyListWhenNoStickyKeysAreActive() { testScope.runTest { val stickyKeys by collectLastValue(viewModel.indicatorContent) - keyboardRepository.setIsAnyKeyboardConnected(true) + setStickyKeysActive() setStickyKeys(emptyMap()) @@ -135,7 +175,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { fun passesAllStickyKeysToDialog() { testScope.runTest { val stickyKeys by collectLastValue(viewModel.indicatorContent) - keyboardRepository.setIsAnyKeyboardConnected(true) + setStickyKeysActive() setStickyKeys(mapOf( ALT to false, @@ -154,7 +194,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { fun showsOnlyLockedStateIfKeyIsStickyAndLocked() { testScope.runTest { val stickyKeys by collectLastValue(viewModel.indicatorContent) - keyboardRepository.setIsAnyKeyboardConnected(true) + setStickyKeysActive() setStickyKeys(mapOf( ALT to false, @@ -168,7 +208,7 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { fun doesNotChangeOrderOfKeysIfTheyBecomeLocked() { testScope.runTest { val stickyKeys by collectLastValue(viewModel.indicatorContent) - keyboardRepository.setIsAnyKeyboardConnected(true) + setStickyKeysActive() setStickyKeys(mapOf( META to false, @@ -186,6 +226,11 @@ class StickyKeysIndicatorViewModelTest : SysuiTestCase() { } } + private fun setStickyKeysActive() { + keyboardRepository.setIsAnyKeyboardConnected(true) + setStickyKeySetting(enabled = true) + } + private fun TestScope.setStickyKeys(keys: Map<ModifierKey, Boolean>) { runCurrent() verify(inputManager).registerStickyModifierStateListener(any(), captor.capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 1183964c39d2..14cae0b89a49 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -357,6 +357,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mock(FoldGracePeriodProvider.class); mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider; when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(true); + when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true); // GIVEN keyguard is not enabled and isn't showing mViewMediator.onSystemReady(); @@ -375,12 +376,40 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { @Test @TestableLooper.RunWithLooper(setAsMainLooper = true) + public void doNotShowKeyguard_deviceNotProvisioned() { + // GIVEN feature is enabled + final FoldGracePeriodProvider mockedFoldGracePeriodProvider = + mock(FoldGracePeriodProvider.class); + mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider; + when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(true); + + // GIVEN keyguard is not enabled and isn't showing + mViewMediator.onSystemReady(); + mViewMediator.setKeyguardEnabled(false); + TestableLooper.get(this).processAllMessages(); + captureKeyguardUpdateMonitorCallback(); + assertFalse(mViewMediator.isShowingAndNotOccluded()); + + // WHEN device is NOT provisioned + when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(false); + + // WHEN showKeyguard is requested + mViewMediator.showDismissibleKeyguard(); + + // THEN keyguard is NOT shown + TestableLooper.get(this).processAllMessages(); + assertFalse(mViewMediator.isShowingAndNotOccluded()); + } + + @Test + @TestableLooper.RunWithLooper(setAsMainLooper = true) public void showKeyguardAfterKeyguardNotEnabled_featureNotEnabled() { // GIVEN feature is NOT enabled final FoldGracePeriodProvider mockedFoldGracePeriodProvider = mock(FoldGracePeriodProvider.class); mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider; when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(false); + when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true); // GIVEN keyguard is not enabled and isn't showing mViewMediator.onSystemReady(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt index 75994da6c934..ad2ae8b41af9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt @@ -19,7 +19,7 @@ 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.domain.interactor.primaryBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -65,7 +65,7 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val shadeRepository = kosmos.shadeRepository private val sysuiStatusBarStateController = kosmos.sysuiStatusBarStateController - private val primaryBouncerInteractor = kosmos.primaryBouncerInteractor + private val primaryBouncerInteractor = kosmos.mockPrimaryBouncerInteractor private val underTest = kosmos.bouncerToGoneFlows @Before diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt index f5a70f07c968..8e0541008f59 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt @@ -20,6 +20,7 @@ import android.view.Display import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.settings.FakeDisplayTracker +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -29,7 +30,13 @@ import org.junit.runners.JUnit4 @RunWith(JUnit4::class) class SysUiStateExtTest : SysuiTestCase() { - private val underTest = SysUiState(FakeDisplayTracker(context)) + private val kosmos = testKosmos() + + private val underTest = + SysUiState( + FakeDisplayTracker(context), + kosmos.sceneContainerPlugin, + ) @Test fun updateFlags() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java index 1a93adc7a631..f03f4f7375f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.settings.FakeDisplayTracker; import org.junit.Before; @@ -42,13 +43,15 @@ public class SysUiStateTest extends SysuiTestCase { private static final int FLAG_4 = 1 << 3; private static final int DISPLAY_ID = DEFAULT_DISPLAY; + private KosmosJavaAdapter mKosmos; private SysUiState.SysUiStateCallback mCallback; private SysUiState mFlagsContainer; @Before public void setup() { FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext); - mFlagsContainer = new SysUiState(displayTracker); + mKosmos = new KosmosJavaAdapter(this); + mFlagsContainer = new SysUiState(displayTracker, mKosmos.getSceneContainerPlugin()); mCallback = mock(SysUiState.SysUiStateCallback.class); mFlagsContainer.addCallback(mCallback); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java index 8d306cceeaa7..28d35ce24f4b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java @@ -41,6 +41,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; @@ -56,6 +57,8 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.KeyguardStateController; +import dagger.Lazy; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -65,8 +68,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; import java.util.Optional; - -import dagger.Lazy; +import java.util.concurrent.Executor; /** * Tests for {@link NavBarHelper}. @@ -123,6 +125,8 @@ public class NavBarHelperTest extends SysuiTestCase { SYSUI_STATE_A11Y_BUTTON_CLICKABLE | SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; private NavBarHelper mNavBarHelper; + private final Executor mSynchronousExecutor = runnable -> runnable.run(); + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -140,7 +144,8 @@ public class NavBarHelperTest extends SysuiTestCase { mSystemActions, mOverviewProxyService, mAssistManagerLazy, () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class), mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker, - mDisplayTracker, mNotificationShadeWindowController, mDumpManager, mCommandQueue); + mDisplayTracker, mNotificationShadeWindowController, mDumpManager, mCommandQueue, + mSynchronousExecutor); } @@ -266,7 +271,8 @@ public class NavBarHelperTest extends SysuiTestCase { @Test public void initNavBarHelper_buttonModeNavBar_a11yButtonClickableState() { when(mAccessibilityManager.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_BUTTON)).thenReturn(createFakeShortcutTargets()); + ShortcutConstants.UserShortcutType.SOFTWARE)).thenReturn( + createFakeShortcutTargets()); mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater); @@ -291,7 +297,8 @@ public class NavBarHelperTest extends SysuiTestCase { ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); when(mAccessibilityManager.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_BUTTON)).thenReturn(createFakeShortcutTargets()); + ShortcutConstants.UserShortcutType.SOFTWARE)).thenReturn( + createFakeShortcutTargets()); mAccessibilityServicesStateChangeListener.onAccessibilityServicesStateChanged( mAccessibilityManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index ddceed62fdeb..db5bd9b13dd0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -228,6 +228,8 @@ public class NavigationBarTest extends SysuiTestCase { @Rule public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck(); + private final Executor mSynchronousExecutor = runnable -> runnable.run(); + @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); @@ -269,7 +271,7 @@ public class NavigationBarTest extends SysuiTestCase { mEdgeBackGestureHandlerFactory, mock(IWindowManager.class), mock(UserTracker.class), mock(DisplayTracker.class), mNotificationShadeWindowController, mock(DumpManager.class), - mock(CommandQueue.class))); + mock(CommandQueue.class), mSynchronousExecutor)); mNavigationBar = createNavBar(mContext); mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal); }); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt index 60eb3aec190f..40eccad94340 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt @@ -37,7 +37,7 @@ class LeftRightArrowPressedListenerTest : SysuiTestCase() { object : Consumer<Int> { var lastValue: Int? = null - override fun accept(keyCode: Int?) { + override fun accept(keyCode: Int) { lastValue = keyCode } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt index e9f21329bfbc..10d6ebf11be7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt @@ -17,16 +17,21 @@ package com.android.systemui.recents import android.content.ComponentName +import android.content.Context import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.os.PowerManager +import android.os.Process; +import android.os.UserHandle import android.testing.AndroidTestingRunner import android.testing.TestableContext import android.testing.TestableLooper import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.app.AssistUtils import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlags @@ -35,6 +40,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager import com.android.systemui.model.SysUiState +import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.navigationbar.NavigationBarController import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP @@ -50,6 +56,7 @@ import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_GOING_TO import com.android.systemui.shared.system.QuickStepContract.WAKEFULNESS_WAKING import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.testKosmos import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -66,10 +73,14 @@ import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt +import org.mockito.Mockito.atLeast import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.intThat import org.mockito.Mockito.mock +import org.mockito.Mockito.spy +import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -79,11 +90,12 @@ class OverviewProxyServiceTest : SysuiTestCase() { @Main private val executor: Executor = MoreExecutors.directExecutor() + private val kosmos = testKosmos() private lateinit var subject: OverviewProxyService - private val dumpManager = DumpManager() + @Mock private val dumpManager = DumpManager() private val displayTracker = FakeDisplayTracker(mContext) private val fakeSystemClock = FakeSystemClock() - private val sysUiState = SysUiState(displayTracker) + private val sysUiState = SysUiState(displayTracker, kosmos.sceneContainerPlugin) private val featureFlags = FakeFeatureFlags() private val wakefulnessLifecycle = WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager) @@ -109,6 +121,8 @@ class OverviewProxyServiceTest : SysuiTestCase() { @Mock private lateinit var unfoldTransitionProgressForwarder: Optional<UnfoldTransitionProgressForwarder> + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher @Before fun setUp() { @@ -135,31 +149,7 @@ class OverviewProxyServiceTest : SysuiTestCase() { com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR, ) - subject = - OverviewProxyService( - context, - executor, - commandQueue, - shellInterface, - { navBarController }, - { shadeViewController }, - screenPinningRequest, - navModeController, - statusBarWinController, - sysUiState, - mock(), - userTracker, - wakefulnessLifecycle, - uiEventLogger, - displayTracker, - sysuiUnlockAnimationController, - inWindowLauncherUnlockAnimationManager, - assistUtils, - featureFlags, - FakeSceneContainerFlags(), - dumpManager, - unfoldTransitionProgressForwarder - ) + subject = createOverviewProxyService(context) } @After @@ -212,4 +202,66 @@ class OverviewProxyServiceTest : SysuiTestCase() { intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_GOING_TO_SLEEP } ) } + + @Test + fun connectToOverviewService_primaryUser_expectBindService() { + val mockitoSession = ExtendedMockito.mockitoSession() + .spyStatic(Process::class.java) + .startMocking() + try { + `when`(Process.myUserHandle()).thenReturn(UserHandle.SYSTEM) + val spyContext = spy(context) + val ops = createOverviewProxyService(spyContext) + ops.startConnectionToCurrentUser() + verify(spyContext, atLeast(1)).bindServiceAsUser(any(), any(), + anyInt(), any()) + } finally { + mockitoSession.finishMocking() + } + } + + @Test + fun connectToOverviewService_nonPrimaryUser_expectNoBindService() { + val mockitoSession = ExtendedMockito.mockitoSession() + .spyStatic(Process::class.java) + .startMocking() + try { + `when`(Process.myUserHandle()).thenReturn(UserHandle.of(12345)) + val spyContext = spy(context) + val ops = createOverviewProxyService(spyContext) + ops.startConnectionToCurrentUser() + verify(spyContext, times(0)).bindServiceAsUser(any(), any(), + anyInt(), any()) + } finally { + mockitoSession.finishMocking() + } + } + + private fun createOverviewProxyService(ctx: Context) : OverviewProxyService { + return OverviewProxyService( + ctx, + executor, + commandQueue, + shellInterface, + { navBarController }, + { shadeViewController }, + screenPinningRequest, + navModeController, + statusBarWinController, + sysUiState, + mock(), + userTracker, + wakefulnessLifecycle, + uiEventLogger, + displayTracker, + sysuiUnlockAnimationController, + inWindowLauncherUnlockAnimationManager, + assistUtils, + featureFlags, + FakeSceneContainerFlags(), + dumpManager, + unfoldTransitionProgressForwarder, + broadcastDispatcher + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index a2aed988a423..9ce77e58a5f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -28,10 +28,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.app.ActivityOptions.LaunchCookie; import android.app.Notification; import android.app.NotificationManager; import android.content.Intent; -import android.os.Binder; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -146,7 +146,7 @@ public class RecordingServiceTest extends SysuiTestCase { @Test public void testLogStartPartialRecording() { - MediaProjectionCaptureTarget target = new MediaProjectionCaptureTarget(new Binder()); + MediaProjectionCaptureTarget target = new MediaProjectionCaptureTarget(new LaunchCookie()); Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, target); mRecordingService.onStartCommand(startIntent, 0, 0); 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 6681ceee3d09..22b05be507c6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -514,7 +514,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { } whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(false) - whenever(mGlanceableHubContainerController.enabledState()) + whenever(mGlanceableHubContainerController.communalAvailable()) .thenReturn(MutableStateFlow(false)) val mockCommunalPlaceholder = mock(View::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 316f2b99b27b..849a13be58ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -41,6 +41,8 @@ import static org.mockito.Mockito.when; import static java.util.Collections.emptySet; +import static kotlinx.coroutines.flow.FlowKt.flowOf; + import android.app.ActivityManager; import android.app.IWallpaperManager; import android.app.WallpaperManager; @@ -72,13 +74,11 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.TestScopeProvider; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; @@ -93,6 +93,10 @@ import com.android.systemui.charging.WiredChargingRippleController; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.communal.data.repository.CommunalRepository; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; +import com.android.systemui.communal.shared.model.CommunalSceneKey; +import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; @@ -103,6 +107,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.log.LogBuffer; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.notetask.NoteTaskController; @@ -131,7 +136,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LightRevealScrim; import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -144,13 +148,10 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository; -import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; -import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; @@ -169,7 +170,6 @@ import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; -import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.util.EventLog; @@ -201,6 +201,8 @@ import java.util.Optional; import javax.inject.Provider; +import kotlinx.coroutines.test.TestScope; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -209,11 +211,17 @@ public class CentralSurfacesImplTest extends SysuiTestCase { private static final int FOLD_STATE_FOLDED = 0; private static final int FOLD_STATE_UNFOLDED = 1; + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + private CentralSurfacesImpl mCentralSurfaces; private FakeMetricsLogger mMetricsLogger; private PowerManager mPowerManager; private VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider; + + private final TestScope mTestScope = mKosmos.getTestScope(); + private final CommunalInteractor mCommunalInteractor = mKosmos.getCommunalInteractor(); + private final CommunalRepository mCommunalRepository = mKosmos.getCommunalRepository(); @Mock private NotificationsController mNotificationsController; @Mock private LightBarController mLightBarController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -235,12 +243,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; @Mock private BiometricUnlockController mBiometricUnlockController; @Mock private AuthRippleController mAuthRippleController; - @Mock private NotificationListener mNotificationListener; @Mock private KeyguardViewMediator mKeyguardViewMediator; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private StatusBarStateControllerImpl mStatusBarStateController; - @Mock private ShadeExpansionStateManager mShadeExpansionStateManager; @Mock private BatteryController mBatteryController; @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private NotificationLaunchAnimatorControllerProvider mNotifLaunchAnimControllerProvider; @@ -262,12 +268,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private PulseExpansionHandler mPulseExpansionHandler; @Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock private AutoHideController mAutoHideController; @Mock private StatusBarWindowController mStatusBarWindowController; @Mock private Provider<CollapsedStatusBarFragment> mCollapsedStatusBarFragmentProvider; @Mock private StatusBarWindowStateController mStatusBarWindowStateController; - @Mock private UserSwitcherController mUserSwitcherController; @Mock private Bubbles mBubbles; @Mock private NoteTaskController mNoteTaskController; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @@ -295,7 +299,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private WallpaperController mWallpaperController; @Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; @Mock private LockscreenShadeTransitionController mLockscreenTransitionController; - @Mock private NotificationVisibilityProvider mVisibilityProvider; @Mock private WallpaperManager mWallpaperManager; @Mock private IWallpaperManager mIWallpaperManager; @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @@ -304,8 +307,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private OperatorNameViewController mOperatorNameViewController; @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory; @Mock private ActivityLaunchAnimator mActivityLaunchAnimator; - @Mock private NotifLiveDataStore mNotifLiveDataStore; - @Mock private InteractionJankMonitor mJankMonitor; @Mock private DeviceStateManager mDeviceStateManager; @Mock private WiredChargingRippleController mWiredChargingRippleController; @Mock private Lazy<CameraLauncher> mCameraLauncherLazy; @@ -465,30 +466,28 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mKeyguardBypassController, mKeyguardStateController, mHeadsUpManager, - mDynamicPrivacyController, new FalsingManagerFake(), new FalsingCollectorFake(), mBroadcastDispatcher, mNotificationGutsManager, - mVisualInterruptionDecisionProvider, new ShadeExpansionStateManager(), mKeyguardViewMediator, new DisplayMetrics(), mMetricsLogger, mShadeLogger, - new JavaAdapter(TestScopeProvider.getTestScope()), + new JavaAdapter(mTestScope), mUiBgExecutor, mNotificationPanelViewController, mNotificationMediaManager, mLockscreenUserManager, mRemoteInputManager, mQuickSettingsController, - mUserSwitcherController, mBatteryController, mColorExtractor, mScreenLifecycle, mWakefulnessLifecycle, mPowerInteractor, + mCommunalInteractor, mStatusBarStateController, Optional.of(mBubbles), () -> mNoteTaskController, @@ -542,7 +541,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mWallpaperManager, Optional.of(mStartingSurface), mActivityLaunchAnimator, - mJankMonitor, mDeviceStateManager, mWiredChargingRippleController, mDreamManager, @@ -838,6 +836,25 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test + public void testEnteringGlanceableHub_updatesScrim() { + // Transition to the glanceable hub. + mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( + CommunalSceneKey.Communal.INSTANCE))); + mTestScope.getTestScheduler().runCurrent(); + + // ScrimState also transitions. + verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB); + + // Transition away from the glanceable hub. + mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( + CommunalSceneKey.Blank.INSTANCE))); + mTestScope.getTestScheduler().runCurrent(); + + // ScrimState goes back to UNLOCKED. + verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any()); + } + + @Test public void testShowKeyguardImplementation_setsState() { when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index b3a47d77a307..3bde6e36a51f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -50,6 +51,7 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.testing.ViewUtils; import android.util.MathUtils; import android.view.View; @@ -58,13 +60,13 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.TestScopeProvider; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.KeyguardState; @@ -72,6 +74,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.scrim.ScrimView; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator; @@ -102,7 +105,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.test.TestScope; @RunWith(AndroidTestingRunner.class) @@ -111,13 +113,14 @@ import kotlinx.coroutines.test.TestScope; public class ScrimControllerTest extends SysuiTestCase { @Rule public Expect mExpect = Expect.create(); + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); private final FakeConfigurationController mConfigurationController = new FakeConfigurationController(); private final LargeScreenShadeInterpolator mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator(); - private final TestScope mTestScope = TestScopeProvider.getTestScope(); + private final TestScope mTestScope = mKosmos.getTestScope(); private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope()); private ScrimController mScrimController; @@ -144,10 +147,12 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @Mock private AlternateBouncerToGoneTransitionViewModel mAlternateBouncerToGoneTransitionViewModel; - @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final KeyguardTransitionInteractor mKeyguardTransitionInteractor = + mKosmos.getKeyguardTransitionInteractor(); + private final FakeKeyguardTransitionRepository mKeyguardTransitionRepository = + mKosmos.getKeyguardTransitionRepository(); @Mock private KeyguardInteractor mKeyguardInteractor; private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository(); - @Mock private CoroutineDispatcher mMainDispatcher; @Mock private TypedArray mMockTypedArray; // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The @@ -264,8 +269,6 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDelayedWakeLockFactory.create(any(String.class))).thenReturn(mWakeLock); when(mDockManager.isDocked()).thenReturn(false); - when(mKeyguardTransitionInteractor.transition(any(), any())) - .thenReturn(emptyFlow()); when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha()) .thenReturn(emptyFlow()); when(mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha()) @@ -291,13 +294,16 @@ public class ScrimControllerTest extends SysuiTestCase { mKeyguardTransitionInteractor, mKeyguardInteractor, mWallpaperRepository, - mMainDispatcher, + mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); + // Attach behind scrim so flows that are collecting on it start running. + ViewUtils.attachView(mScrimBehind); + mScrimController.setHasBackdrop(false); mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); @@ -628,6 +634,164 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void lockscreenToHubTransition_setsBehindScrimAlpha() { + // Start on lockscreen. + mScrimController.transitionTo(ScrimState.KEYGUARD); + finishAnimationsImmediately(); + + // Behind scrim starts at default alpha. + final float transitionProgress = 0f; + float expectedAlpha = ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + transitionProgress, + TransitionState.STARTED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim fades out as transition runs. + final float runningProgress = 0.2f; + expectedAlpha = (1 - runningProgress) * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + runningProgress, + TransitionState.RUNNING + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim invisible at end of transition. + final float finishedProgress = 1f; + expectedAlpha = 0f; + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + finishedProgress, + TransitionState.FINISHED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + } + + @Test + public void hubToLockscreenTransition_setsViewAlpha() { + // Start on glanceable hub. + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // Behind scrim starts at 0 alpha. + final float transitionProgress = 0f; + float expectedAlpha = 0f; + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + transitionProgress, + TransitionState.STARTED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim fades in as transition runs. + final float runningProgress = 0.2f; + expectedAlpha = runningProgress * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + runningProgress, + TransitionState.RUNNING + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim at default visibility at end of transition. + final float finishedProgress = 1f; + expectedAlpha = finishedProgress * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + finishedProgress, + TransitionState.FINISHED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + } + + @Test + public void transitionToHub() { + mScrimController.setRawPanelExpansionFraction(0f); + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN); + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims transparent on the hub. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test + public void openBouncerOnHub() { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + + // Open the bouncer. + mScrimController.setRawPanelExpansionFraction(0f); + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_VISIBLE); + finishAnimationsImmediately(); + + // Only behind widget is visible. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, OPAQUE)); + + // Bouncer is closed. + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN); + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims are transparent. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test + public void openShadeOnHub() { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + + // Open the shade. + mScrimController.transitionTo(SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); + finishAnimationsImmediately(); + + // Shade scrims are visible. + assertScrimAlpha(Map.of( + mNotificationsScrim, OPAQUE, + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE)); + + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims are transparent. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test public void onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor() { assertEquals(BOUNCER.getBehindTint(), 0x112233); mSurfaceColor = 0x223344; @@ -1000,7 +1164,7 @@ public class ScrimControllerTest extends SysuiTestCase { mKeyguardTransitionInteractor, mKeyguardInteractor, mWallpaperRepository, - mMainDispatcher, + mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); @@ -1266,7 +1430,7 @@ public class ScrimControllerTest extends SysuiTestCase { ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, BOUNCER, ScrimState.DREAMING, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED, SHADE_LOCKED, ScrimState.AUTH_SCRIMMED, - ScrimState.AUTH_SCRIMMED_SHADE)); + ScrimState.AUTH_SCRIMMED_SHADE, ScrimState.GLANCEABLE_HUB)); for (ScrimState state : ScrimState.values()) { if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) { @@ -1683,6 +1847,26 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void notificationBoundsTopGetsPassedToKeyguard() { + mScrimController.transitionTo(SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); + finishAnimationsImmediately(); + + mScrimController.setNotificationsBounds(0f, 100f, 0f, 0f); + verify(mKeyguardInteractor).setTopClippingBounds(eq(100)); + } + + @Test + public void notificationBoundsTopDoesNotGetPassedToKeyguardWhenNotifScrimIsNotVisible() { + mScrimController.setKeyguardOccluded(true); + mScrimController.transitionTo(ScrimState.KEYGUARD); + finishAnimationsImmediately(); + + mScrimController.setNotificationsBounds(0f, 100f, 0f, 0f); + verify(mKeyguardInteractor).setTopClippingBounds(eq(null)); + } + + @Test public void transitionToDreaming() { mScrimController.setRawPanelExpansionFraction(0f); mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt index 5b5819d649b4..77e48bff04de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt @@ -102,29 +102,6 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { } @Test - fun satelliteManagerThrows_checkSupportDoesNotCrash() = - testScope.runTest { - whenever(satelliteManager.requestIsSatelliteSupported(any(), any())) - .thenThrow(IllegalStateException()) - - systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME) - - underTest = - DeviceBasedSatelliteRepositoryImpl( - Optional.of(satelliteManager), - dispatcher, - testScope.backgroundScope, - FakeLogBuffer.Factory.create(), - systemClock, - ) - - runCurrent() - - // Creating the repo does not crash, and we consider the feature not to be supported - assertThat(underTest.satelliteSupport.value).isEqualTo(SatelliteSupport.NotSupported) - } - - @Test fun connectionState_mapsFromSatelliteModemState() = testScope.runTest { setupDefaultRepo() @@ -132,7 +109,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { runCurrent() val callback = withArgCaptor<SatelliteModemStateCallback> { - verify(satelliteManager).registerForSatelliteModemStateChanged(any(), capture()) + verify(satelliteManager).registerForModemStateChanged(any(), capture()) } // Mapping from modem state to SatelliteConnectionState is rote, just run all of the @@ -207,7 +184,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { null } .`when`(satelliteManager) - .requestIsSatelliteCommunicationAllowedForCurrentLocation( + .requestIsCommunicationAllowedForCurrentLocation( any(), any<OutcomeReceiver<Boolean, SatelliteException>>() ) @@ -230,7 +207,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { null } .`when`(satelliteManager) - .requestIsSatelliteCommunicationAllowedForCurrentLocation( + .requestIsCommunicationAllowedForCurrentLocation( any(), any<OutcomeReceiver<Boolean, SatelliteException>>() ) @@ -253,7 +230,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { null } .`when`(satelliteManager) - .requestIsSatelliteCommunicationAllowedForCurrentLocation( + .requestIsCommunicationAllowedForCurrentLocation( any(), any<OutcomeReceiver<Boolean, SatelliteException>>() ) @@ -297,7 +274,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { null } .`when`(satelliteManager) - .requestIsSatelliteCommunicationAllowedForCurrentLocation( + .requestIsCommunicationAllowedForCurrentLocation( any(), any<OutcomeReceiver<Boolean, SatelliteException>>() ) @@ -330,7 +307,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { null } .`when`(satelliteManager) - .requestIsSatelliteCommunicationAllowedForCurrentLocation( + .requestIsCommunicationAllowedForCurrentLocation( any(), any<OutcomeReceiver<Boolean, SatelliteException>>() ) @@ -356,7 +333,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { val signalStrength by collectLastValue(underTest.signalStrength) // THEN the manager is not asked for the information, and default values are returned - verify(satelliteManager, never()).registerForSatelliteModemStateChanged(any(), any()) + verify(satelliteManager, never()).registerForModemStateChanged(any(), any()) verify(satelliteManager, never()).registerForNtnSignalStrengthChanged(any(), any()) } @@ -382,7 +359,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { runCurrent() // THEN we finally register with the satellite manager - verify(satelliteManager).registerForSatelliteModemStateChanged(any(), any()) + verify(satelliteManager).registerForModemStateChanged(any(), any()) } private fun setUpRepo( @@ -396,7 +373,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { callback.onResult(satelliteSupported) } .whenever(satelliteManager) - .requestIsSatelliteSupported(any(), any()) + .requestIsSupported(any(), any()) systemClock.setUptimeMillis(Process.getStartUptimeMillis() + uptime) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt index e010b8672580..d465b47e93b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.pipeline.satellite.domain.interactor +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import androidx.test.filters.SmallTest import com.android.internal.telephony.flags.Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG import com.android.systemui.SysuiTestCase @@ -49,8 +51,6 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { @Before fun setUp() { - mSetFlagsRule.enableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) - underTest = DeviceBasedSatelliteInteractor( repo, @@ -60,6 +60,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun isSatelliteAllowed_falseWhenNotAllowed() = testScope.runTest { val latest by collectLastValue(underTest.isSatelliteAllowed) @@ -72,6 +73,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun isSatelliteAllowed_trueWhenAllowed() = testScope.runTest { val latest by collectLastValue(underTest.isSatelliteAllowed) @@ -84,10 +86,10 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun isSatelliteAllowed_offWhenFlagIsOff() = testScope.runTest { // GIVEN feature is disabled - mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) // Remake the interactor so the flag is read underTest = @@ -107,6 +109,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun connectionState_matchesRepositoryValue() = testScope.runTest { val latest by collectLastValue(underTest.connectionState) @@ -129,10 +132,10 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun connectionState_offWhenFeatureIsDisabled() = testScope.runTest { // GIVEN the flag is disabled - mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) // Remake the interactor so the flag is read underTest = @@ -164,6 +167,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun signalStrength_matchesRepo() = testScope.runTest { val latest by collectLastValue(underTest.signalStrength) @@ -182,10 +186,10 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun signalStrength_zeroWhenDisabled() = testScope.runTest { // GIVEN the flag is enabled - mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) // Remake the interactor so the flag is read underTest = @@ -212,6 +216,19 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) + fun areAllConnectionsOutOfService_noConnections_yes() = + testScope.runTest { + val latest by collectLastValue(underTest.areAllConnectionsOutOfService) + + // GIVEN, 0 connections + + // THEN the value is propagated to this interactor + assertThat(latest).isTrue() + } + + @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun areAllConnectionsOutOfService_twoConnectionsOos_yes() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) @@ -229,6 +246,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun areAllConnectionsOutOfService_oneConnectionOos_yes() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) @@ -244,6 +262,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun areAllConnectionsOutOfService_oneConnectionInService_no() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) @@ -259,6 +278,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun areAllConnectionsOutOfService_twoConnectionsOneInService_no() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) @@ -276,6 +296,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun areAllConnectionsOutOfService_twoConnectionsInService_no() = testScope.runTest { val latest by collectLastValue(underTest.areAllConnectionsOutOfService) @@ -293,10 +314,10 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) fun areAllConnectionsOutOfService_falseWhenFlagIsOff() = testScope.runTest { // GIVEN the flag is disabled - mSetFlagsRule.disableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG) // Remake the interactor so the flag is read underTest = diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index 457acd214222..b58a41c89a4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -190,7 +190,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mWakefulnessLifecycle.dispatchFinishedWakingUp(); mThemeOverlayController.start(); - verify(mUserTracker).addCallback(mUserTrackerCallback.capture(), eq(mBgExecutor)); + verify(mUserTracker).addCallback(mUserTrackerCallback.capture(), eq(mMainExecutor)); verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null), eq(UserHandle.USER_ALL)); verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiver.capture(), any(), 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 98f3ede850f4..7eba3f0602d5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -548,7 +548,7 @@ public class BubblesTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); - mSysUiState = new SysUiState(mDisplayTracker); + mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin()); mSysUiState.addCallback(sysUiFlags -> { mSysUiStateBubblesManageMenuExpanded = (sysUiFlags diff --git a/packages/SystemUI/tests/utils/src/android/content/PackageManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/PackageManagerKosmos.kt new file mode 100644 index 000000000000..8901314d8e76 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/content/PackageManagerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content + +import android.content.pm.PackageManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.packageManager by Kosmos.Fixture { mock<PackageManager>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index af7f4c894c7c..b62b3a211e58 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -21,17 +21,25 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.Instrumentation; +import android.content.Context; +import android.content.res.Resources; import android.os.Build; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.MessageQueue; import android.os.ParcelFileDescriptor; +import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.flag.junit.SetFlagsRule; +import android.platform.test.ravenwood.RavenwoodClassRule; +import android.platform.test.ravenwood.RavenwoodRule; +import android.test.mock.MockContext; import android.testing.DexmakerShareClassLoaderRule; import android.testing.LeakCheck; import android.testing.TestWithLooperRule; import android.testing.TestableLooper; import android.util.Log; +import android.util.Singleton; import androidx.annotation.NonNull; import androidx.core.animation.AndroidXAnimatorIsolationRule; @@ -44,17 +52,24 @@ import com.android.systemui.flags.SceneContainerRule; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Rule; import org.mockito.Mockito; import java.io.FileInputStream; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.Future; /** * Base class that does System UI specific setup. */ +// NOTE: This @DisabledOnRavenwood annotation is inherited to all subclasses (unless overridden +// via a more-specific @EnabledOnRavenwood annotation); this means that by default all +// subclasses will be "ignored" when executed on the Ravenwood testing environment; more +// background on Ravenwood is available at go/ravenwood-docs +@DisabledOnRavenwood public abstract class SysuiTestCase { private static final String TAG = "SysuiTestCase"; @@ -66,6 +81,23 @@ public abstract class SysuiTestCase { public AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule = new AndroidXAnimatorIsolationRule(); + /** + * Rule that respects class-level annotations such as {@code @DisabledOnRavenwood} when tests + * are running on Ravenwood; on all other test environments this rule is a no-op passthrough. + */ + @ClassRule(order = Integer.MIN_VALUE + 1) + public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule(); + + /** + * Rule that defines and prepares the Ravenwood environment when tests are running on + * Ravenwood; on all other test environments this rule is a no-op passthrough. + */ + @Rule(order = Integer.MIN_VALUE + 1) + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProcessApp() + .setProvideMainThread(true) + .build(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @@ -78,7 +110,7 @@ public abstract class SysuiTestCase { @NonNull private SysuiTestableContext createTestableContext() { SysuiTestableContext context = new SysuiTestableContext( - InstrumentationRegistry.getContext(), getLeakCheck()); + getTestableContextBase(), getLeakCheck()); if (isRobolectricTest()) { // Manually associate a Display to context for Robolectric test. Similar to b/214297409 return context.createDefaultDisplayContext(); @@ -87,6 +119,43 @@ public abstract class SysuiTestCase { } } + @NonNull + private Context getTestableContextBase() { + if (isRavenwoodTest()) { + // TODO(b/292141694): build out Ravenwood support for Context + // Ravenwood doesn't yet provide a Context, but many SysUI tests assume one exists; + // so here we construct just enough of a Context to be useful; this will be replaced + // as more of the Ravenwood environment is built out + return new MockContext() { + @Override + public void setTheme(int resid) { + // TODO(b/318393625): build out Ravenwood support for Resources + // until then, ignored as no-op + } + + @Override + public Resources getResources() { + // TODO(b/318393625): build out Ravenwood support for Resources + return Mockito.mock(Resources.class); + } + + private Singleton<Executor> mMainExecutor = new Singleton<>() { + @Override + protected Executor create() { + return new HandlerExecutor(new Handler(Looper.getMainLooper())); + } + }; + + @Override + public Executor getMainExecutor() { + return mMainExecutor.get(); + } + }; + } else { + return InstrumentationRegistry.getContext(); + } + } + @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); @@ -103,17 +172,22 @@ public abstract class SysuiTestCase { public void SysuiSetup() throws Exception { mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver()); mDependency = mSysuiDependency.install(); - mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); - Instrumentation inst = spy(mRealInstrumentation); - when(inst.getContext()).thenAnswer(invocation -> { - throw new RuntimeException( - "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); - }); - when(inst.getTargetContext()).thenAnswer(invocation -> { - throw new RuntimeException( - "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); - }); - InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments()); + // TODO(b/292141694): build out Ravenwood support for Instrumentation + // Ravenwood doesn't yet provide Instrumentation, so we sidestep this global configuration + // step; any tests that rely on it are already being excluded on Ravenwood + if (!isRavenwoodTest()) { + mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); + Instrumentation inst = spy(mRealInstrumentation); + when(inst.getContext()).thenAnswer(invocation -> { + throw new RuntimeException( + "Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); + }); + when(inst.getTargetContext()).thenAnswer(invocation -> { + throw new RuntimeException( + "Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"); + }); + InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments()); + } } protected boolean shouldFailOnLeakedReceiver() { @@ -209,7 +283,11 @@ public abstract class SysuiTestCase { } public static boolean isRobolectricTest() { - return Build.FINGERPRINT.contains("robolectric"); + return !isRavenwoodTest() && Build.FINGERPRINT.contains("robolectric"); + } + + public static boolean isRavenwoodTest() { + return RavenwoodRule.isOnRavenwood(); } private static final void validateThread(Looper l) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt index d89d7b00592e..364d3b236ef5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt @@ -34,10 +34,16 @@ class SysuiTestDependency( // is missing (constructing the actual one would throw). // TODO(b/219008720): Remove this. dependency.injectMockDependency(SystemUIDialogManager::class.java) - dependency.injectTestDependency( - DialogLaunchAnimator::class.java, - fakeDialogLaunchAnimator() - ) + + // TODO(b/292141694): build out Ravenwood support for UI animations + // Ravenwood doesn't yet provide UI animations, so we sidestep this global configuration + // step; any tests that rely on it are already being excluded under Ravenwood + if (!SysuiTestCase.isRavenwoodTest()) { + dependency.injectTestDependency( + DialogLaunchAnimator::class.java, + fakeDialogLaunchAnimator() + ) + } // Many tests end up creating a BroadcastDispatcher. Instead, give them a fake that will // record receivers registered. They are not actually leaked as they are kept just as a weak 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 7c8a7c8d1c87..2bd104dd375d 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,12 +19,14 @@ package com.android.systemui.authentication.domain.interactor 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.domain.interactor.selectedUserInteractor val Kosmos.authenticationInteractor by Kosmos.Fixture { AuthenticationInteractor( applicationScope = applicationCoroutineScope, + backgroundDispatcher = testDispatcher, repository = authenticationRepository, selectedUserInteractor = selectedUserInteractor, ) diff --git a/core/java/android/credentials/ui/UiResult.java b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/UdfpsUtilsKosmos.kt index 692584d1a561..4849fec14a88 100644 --- a/core/java/android/credentials/ui/UiResult.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/UdfpsUtilsKosmos.kt @@ -14,11 +14,9 @@ * limitations under the License. */ -package android.credentials.ui; +package com.android.systemui.biometrics -/** - * Base class for different types of ui results. - * - * @hide - */ -public interface UiResult {} +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.udfpsUtils by Kosmos.Fixture { mock<UdfpsUtils>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryKosmos.kt new file mode 100644 index 000000000000..961022f0f426 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.biometrics.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +val Kosmos.biometricStatusRepository by Fixture { FakeBiometricStatusRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/PromptRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/PromptRepositoryKosmos.kt new file mode 100644 index 000000000000..31fa5d2ed715 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/PromptRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +var Kosmos.promptRepository by Fixture { FakePromptRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorKosmos.kt new file mode 100644 index 000000000000..1493f14d9aa7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.domain.interactor + +import com.android.app.activityTaskManager +import com.android.systemui.biometrics.data.repository.biometricStatusRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +val Kosmos.biometricStatusInteractor by Fixture { + BiometricStatusInteractorImpl( + activityTaskManager = activityTaskManager, + biometricStatusRepository = biometricStatusRepository + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt new file mode 100644 index 000000000000..7f9a71cd149e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.domain.interactor + +import com.android.internal.widget.lockPatternUtils +import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository +import com.android.systemui.biometrics.data.repository.promptRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +val Kosmos.promptSelectorInteractor by Fixture { + PromptSelectorInteractorImpl( + fingerprintPropertyRepository = fingerprintPropertyRepository, + promptRepository = promptRepository, + lockPatternUtils = lockPatternUtils + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt new file mode 100644 index 000000000000..9cbe6337befe --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.ui.viewmodel + +import android.content.applicationContext +import com.android.systemui.biometrics.domain.interactor.displayStateInteractor +import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor +import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor +import com.android.systemui.biometrics.udfpsUtils +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture + +val Kosmos.promptViewModel by Fixture { + PromptViewModel( + displayStateInteractor = displayStateInteractor, + promptSelectorInteractor = promptSelectorInteractor, + context = applicationContext, + udfpsOverlayInteractor = udfpsOverlayInteractor, + udfpsUtils = udfpsUtils + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorKosmos.kt index 06b6cda62806..244ef8d81ebd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorKosmos.kt @@ -16,7 +16,40 @@ package com.android.systemui.bouncer.domain.interactor +import android.content.applicationContext +import com.android.keyguard.keyguardSecurityModel +import com.android.keyguard.keyguardUpdateMonitor +import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository +import com.android.systemui.bouncer.ui.BouncerView +import com.android.systemui.classifier.falsingCollector +import com.android.systemui.concurrency.fakeExecutor +import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.repository.trustRepository import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl +import com.android.systemui.user.domain.interactor.selectedUserInteractor +import com.android.systemui.util.concurrency.mockExecutorHandler import com.android.systemui.util.mockito.mock -var Kosmos.primaryBouncerInteractor by Kosmos.Fixture { mock<PrimaryBouncerInteractor>() } +var Kosmos.mockPrimaryBouncerInteractor by Kosmos.Fixture { mock<PrimaryBouncerInteractor>() } +var Kosmos.primaryBouncerInteractor by + Kosmos.Fixture { + PrimaryBouncerInteractor( + repository = keyguardBouncerRepository, + primaryBouncerView = mock<BouncerView>(), + mainHandler = mockExecutorHandler(executor = fakeExecutor), + keyguardStateController = mock<KeyguardStateControllerImpl>(), + keyguardSecurityModel = keyguardSecurityModel, + primaryBouncerCallbackInteractor = mock<PrimaryBouncerCallbackInteractor>(), + falsingCollector = falsingCollector, + dismissCallbackRegistry = mock<DismissCallbackRegistry>(), + context = applicationContext, + keyguardUpdateMonitor = keyguardUpdateMonitor, + trustRepository = trustRepository, + applicationScope = applicationCoroutineScope, + selectedUserInteractor = selectedUserInteractor, + deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt index 1abf71fde14c..c818e9c8971c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt @@ -24,14 +24,12 @@ import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.smartspace.data.repository.smartspaceRepository import com.android.systemui.user.data.repository.userRepository import com.android.systemui.util.mockito.mock val Kosmos.communalInteractor by Fixture { CommunalInteractor( - applicationScope = applicationCoroutineScope, communalRepository = communalRepository, widgetRepository = communalWidgetRepository, mediaRepository = communalMediaRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt index 0b1fb4074226..5575b05b3874 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt @@ -23,7 +23,7 @@ import com.android.keyguard.keyguardUpdateMonitor import com.android.keyguard.trustManager import com.android.systemui.biometrics.data.repository.facePropertyRepository import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor -import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor import com.android.systemui.deviceentry.data.repository.faceWakeUpTriggersConfig import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository @@ -46,7 +46,7 @@ val Kosmos.deviceEntryFaceAuthInteractor by applicationScope = applicationCoroutineScope, mainDispatcher = testDispatcher, repository = deviceEntryFaceAuthRepository, - primaryBouncerInteractor = { primaryBouncerInteractor }, + primaryBouncerInteractor = { mockPrimaryBouncerInteractor }, alternateBouncerInteractor = alternateBouncerInteractor, keyguardTransitionInteractor = keyguardTransitionInteractor, faceAuthenticationLogger = faceAuthLogger, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinderKosmos.kt new file mode 100644 index 000000000000..2fead91b430a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinderKosmos.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.ui.binder + +import android.content.packageManager +import com.android.keyguard.keyguardUpdateMonitor +import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.util.sensors.asyncSensorManager +import kotlinx.coroutines.ExperimentalCoroutinesApi + +@ExperimentalCoroutinesApi +val Kosmos.liftToRunFaceAuthBinder by + Kosmos.Fixture { + LiftToRunFaceAuthBinder( + scope = applicationCoroutineScope, + packageManager = packageManager, + asyncSensorManager = asyncSensorManager, + keyguardUpdateMonitor = keyguardUpdateMonitor, + keyguardInteractor = keyguardInteractor, + primaryBouncerInteractor = primaryBouncerInteractor, + alternateBouncerInteractor = alternateBouncerInteractor, + deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor, + powerInteractor = powerInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index 0c1dbfebfb34..e20a0ab4190e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -28,9 +28,12 @@ import dagger.Module import java.util.UUID import javax.inject.Inject import junit.framework.Assert.fail +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -150,6 +153,15 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio _transitions.emit(step) } + /** Version of [sendTransitionStep] that's usable from Java tests. */ + fun sendTransitionStepJava( + coroutineScope: CoroutineScope, + step: TransitionStep, + validateStep: Boolean = true + ): Job { + return coroutineScope.launch { sendTransitionStep(step, validateStep) } + } + suspend fun sendTransitionSteps( steps: List<TransitionStep>, testScope: TestScope, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt index c71c1c3ea5f0..ffa4133c7269 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsKosmos.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos @@ -31,7 +31,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.bouncerToGoneFlows by Fixture { BouncerToGoneFlows( statusBarStateController = sysuiStatusBarStateController, - primaryBouncerInteractor = primaryBouncerInteractor, + primaryBouncerInteractor = mockPrimaryBouncerInteractor, keyguardDismissActionInteractor = mock(), featureFlags = featureFlagsClassic, shadeInteractor = shadeInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt index ab28d0d670ef..4ecff73f71ed 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelKosmos.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor import com.android.systemui.flags.featureFlagsClassic import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos @@ -30,7 +30,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.primaryBouncerToGoneTransitionViewModel by Fixture { PrimaryBouncerToGoneTransitionViewModel( statusBarStateController = sysuiStatusBarStateController, - primaryBouncerInteractor = primaryBouncerInteractor, + primaryBouncerInteractor = mockPrimaryBouncerInteractor, keyguardDismissActionInteractor = mock(), featureFlags = featureFlagsClassic, bouncerToGoneFlows = bouncerToGoneFlows, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 321f94468a2e..083de107c971 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -32,6 +32,9 @@ import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteract import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.domain.interactor.powerInteractor @@ -60,6 +63,8 @@ class KosmosJavaAdapter( val bouncerRepository by lazy { kosmos.bouncerRepository } val communalRepository by lazy { kosmos.fakeCommunalRepository } val keyguardRepository by lazy { kosmos.fakeKeyguardRepository } + val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository } + val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor } val powerRepository by lazy { kosmos.fakePowerRepository } val clock by lazy { kosmos.systemClock } val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository } @@ -74,6 +79,7 @@ class KosmosJavaAdapter( val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor } val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } val communalInteractor by lazy { kosmos.communalInteractor } + val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin } init { kosmos.applicationContext = testCase.context diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt new file mode 100644 index 000000000000..b1027b923a26 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SceneContainerPluginKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.model + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.scene.domain.interactor.sceneInteractor + +val Kosmos.sceneContainerPlugin by Fixture { SceneContainerPlugin { sceneInteractor } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorKosmos.kt index 57b272f10c67..d2dd200faa07 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorKosmos.kt @@ -17,7 +17,12 @@ package com.android.systemui.shade.domain.interactor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.data.repository.shadeAnimationRepository var Kosmos.shadeAnimationInteractor: ShadeAnimationInteractor by Kosmos.Fixture { ShadeAnimationInteractorEmptyImpl(shadeAnimationRepository) } +var Kosmos.shadeAnimationInteractorSceneContainerImpl: ShadeAnimationInteractorSceneContainerImpl by + Kosmos.Fixture { + ShadeAnimationInteractorSceneContainerImpl(shadeAnimationRepository, sceneInteractor) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt index 18a2f9482df8..33ed7e61de4d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt @@ -20,3 +20,5 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.util.mockito.mock val Kosmos.configurationController by Kosmos.Fixture { mock<ConfigurationController>() } +val Kosmos.fakeConfigurationController: FakeConfigurationController by + Kosmos.Fixture { FakeConfigurationController() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/AsyncSensorManagerKosmos.kt index dbff63f355c8..117ae8c46d3c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/AsyncSensorManagerKosmos.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.systemui.util.sensors -package com.android.systemui.volume.panel.domain.interactor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock -class ComponentsInteractorTest { - - // TODO(b/318080198) Write tests -} +val Kosmos.asyncSensorManager by Kosmos.Fixture { mock<AsyncSensorManager>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt new file mode 100644 index 000000000000..d3410737a432 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/VolumePanelKosmos.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel + +import android.content.res.mainResources +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.policy.fakeConfigurationController +import com.android.systemui.util.mockito.mock +import com.android.systemui.volume.panel.dagger.factory.KosmosVolumePanelComponentFactory +import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria +import com.android.systemui.volume.panel.domain.TestComponentAvailabilityCriteria +import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor +import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractorImpl +import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey +import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent +import com.android.systemui.volume.panel.ui.composable.ComponentsFactory +import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager +import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel +import javax.inject.Provider + +val Kosmos.mockVolumePanelUiComponent: VolumePanelUiComponent by Kosmos.Fixture { mock {} } +val Kosmos.mockVolumePanelUiComponentProvider: Provider<VolumePanelUiComponent> by + Kosmos.Fixture { Provider { mockVolumePanelUiComponent } } +var Kosmos.componentByKey: Map<VolumePanelComponentKey, Provider<VolumePanelUiComponent>> by + Kosmos.Fixture { emptyMap() } +val Kosmos.componentsFactory: ComponentsFactory by + Kosmos.Fixture { ComponentsFactory(componentByKey) } + +var Kosmos.componentsLayoutManager: ComponentsLayoutManager by Kosmos.Fixture() +var Kosmos.enabledComponents: Collection<VolumePanelComponentKey> by + Kosmos.Fixture { componentByKey.keys } +val Kosmos.unavailableCriteria: Provider<ComponentAvailabilityCriteria> by + Kosmos.Fixture { Provider { TestComponentAvailabilityCriteria(false) } } +val Kosmos.availableCriteria: Provider<ComponentAvailabilityCriteria> by + Kosmos.Fixture { Provider { TestComponentAvailabilityCriteria(true) } } +var Kosmos.defaultCriteria: Provider<ComponentAvailabilityCriteria> by + Kosmos.Fixture { availableCriteria } +var Kosmos.criteriaByKey: Map<VolumePanelComponentKey, Provider<ComponentAvailabilityCriteria>> by + Kosmos.Fixture { emptyMap() } +var Kosmos.componentsInteractor: ComponentsInteractor by + Kosmos.Fixture { + ComponentsInteractorImpl( + enabledComponents, + defaultCriteria, + testScope.backgroundScope, + criteriaByKey, + ) + } + +var Kosmos.volumePanelViewModel: VolumePanelViewModel by + Kosmos.Fixture { + VolumePanelViewModel( + mainResources, + KosmosVolumePanelComponentFactory(this), + fakeConfigurationController, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt new file mode 100644 index 000000000000..49041ed0d652 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/dagger/factory/KosmosVolumePanelComponentFactory.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.dagger.factory + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.volume.panel.componentsFactory +import com.android.systemui.volume.panel.componentsInteractor +import com.android.systemui.volume.panel.componentsLayoutManager +import com.android.systemui.volume.panel.dagger.VolumePanelComponent +import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor +import com.android.systemui.volume.panel.ui.composable.ComponentsFactory +import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager +import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel +import kotlinx.coroutines.CoroutineScope + +class KosmosVolumePanelComponentFactory(private val kosmos: Kosmos) : VolumePanelComponentFactory { + + override fun create(viewModel: VolumePanelViewModel): VolumePanelComponent = + object : VolumePanelComponent { + + override fun coroutineScope(): CoroutineScope = kosmos.testScope.backgroundScope + + override fun componentsInteractor(): ComponentsInteractor = kosmos.componentsInteractor + + override fun componentsFactory(): ComponentsFactory = kosmos.componentsFactory + + override fun componentsLayoutManager(): ComponentsLayoutManager = + kosmos.componentsLayoutManager + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/TestComponentAvailabilityCriteria.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/TestComponentAvailabilityCriteria.kt new file mode 100644 index 000000000000..5ab92745ae69 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/TestComponentAvailabilityCriteria.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.domain + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +class TestComponentAvailabilityCriteria(val isAvailable: Boolean) : ComponentAvailabilityCriteria { + + override fun isAvailable(): Flow<Boolean> = flowOf(isAvailable) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/layout/FakeComponentsLayoutManager.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/layout/FakeComponentsLayoutManager.kt new file mode 100644 index 000000000000..655d8f7ad952 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/layout/FakeComponentsLayoutManager.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.ui.layout + +import com.android.systemui.volume.panel.ui.viewmodel.ComponentState +import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState + +class FakeComponentsLayoutManager( + private val isBottomBar: (components: ComponentState) -> Boolean +) : ComponentsLayoutManager { + + override fun layout( + volumePanelState: VolumePanelState, + components: Collection<ComponentState> + ): ComponentsLayout { + return ComponentsLayout( + components + .filter { componentState -> !isBottomBar(componentState) } + .sortedBy { it.key }, + components.find(isBottomBar)!!, + ) + } +} diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index 3aa9cc84e9f6..155c523a96a7 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -132,7 +132,7 @@ public class CameraExtensionsProxyService extends Service { private static final String CAMERA_EXTENSION_VERSION_NAME = "androidx.camera.extensions.impl.ExtensionVersionImpl"; - private static final String LATEST_VERSION = "1.4.0"; + private static final String LATEST_VERSION = "1.5.0"; // No support for the init sequence private static final String NON_INIT_VERSION_PREFIX = "1.0"; // Support advanced API and latency queries @@ -1693,6 +1693,7 @@ public class CameraExtensionsProxyService extends Service { private final Size mSize; private final int mImageFormat; private final int mDataspace; + private final long mUsage; public OutputSurfaceImplStub(OutputSurface outputSurface) { mSurface = outputSurface.surface; @@ -1700,8 +1701,10 @@ public class CameraExtensionsProxyService extends Service { mImageFormat = outputSurface.imageFormat; if (mSurface != null) { mDataspace = SurfaceUtils.getSurfaceDataspace(mSurface); + mUsage = SurfaceUtils.getSurfaceUsage(mSurface); } else { mDataspace = -1; + mUsage = 0; } } @@ -1724,6 +1727,11 @@ public class CameraExtensionsProxyService extends Service { public int getDataspace() { return mDataspace; } + + @Override + public long getUsage() { + return mUsage; + } } private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub implements @@ -2471,6 +2479,11 @@ public class CameraExtensionsProxyService extends Service { ret.size.height = imageReaderOutputConfig.getSize().getHeight(); ret.imageFormat = imageReaderOutputConfig.getImageFormat(); ret.capacity = imageReaderOutputConfig.getMaxImages(); + if (EFV_SUPPORTED) { + ret.usage = imageReaderOutputConfig.getUsage(); + } else { + ret.usage = 0; + } } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) { MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig = (MultiResolutionImageReaderOutputConfigImpl) output; diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index e013a3e41896..1ac69f6c4fc8 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -30,6 +30,9 @@ java_library { "junit-src/**/*.java", "junit-impl-src/**/*.java", ], + static_libs: [ + "androidx.test.monitor-for-device", + ], libs: [ "framework-minus-apex.ravenwood", "junit", @@ -61,3 +64,17 @@ java_host_for_device { "core-xml-for-host", ], } + +java_host_for_device { + name: "androidx.test.monitor-for-device", + libs: [ + "androidx.test.monitor-for-host", + ], +} + +java_device_for_host { + name: "androidx.test.monitor-for-host", + libs: [ + "androidx.test.monitor", + ], +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index 91c522e82cce..a797b1d61b2a 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -16,13 +16,36 @@ package android.platform.test.ravenwood; +import android.app.Instrumentation; +import android.os.Bundle; import android.os.HandlerThread; import android.os.Looper; +import androidx.test.platform.app.InstrumentationRegistry; + +import java.io.PrintStream; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + public class RavenwoodRuleImpl { private static final String MAIN_THREAD_NAME = "RavenwoodMain"; - public static boolean isUnderRavenwood() { + /** + * When enabled, attempt to dump all thread stacks just before we hit the + * overall Tradefed timeout, to aid in debugging deadlocks. + */ + private static final boolean ENABLE_TIMEOUT_STACKS = false; + private static final int TIMEOUT_MILLIS = 9_000; + + private static final ScheduledExecutorService sTimeoutExecutor = + Executors.newScheduledThreadPool(1); + + private static ScheduledFuture<?> sPendingTimeout; + + public static boolean isOnRavenwood() { return true; } @@ -41,9 +64,22 @@ public class RavenwoodRuleImpl { main.start(); Looper.setMainLooperForTest(main.getLooper()); } + + InstrumentationRegistry.registerInstance(new Instrumentation(), Bundle.EMPTY); + + if (ENABLE_TIMEOUT_STACKS) { + sPendingTimeout = sTimeoutExecutor.schedule(RavenwoodRuleImpl::dumpStacks, + TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } } public static void reset(RavenwoodRule rule) { + if (ENABLE_TIMEOUT_STACKS) { + sPendingTimeout.cancel(false); + } + + InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); + if (rule.mProvideMainThread) { Looper.getMainLooper().quit(); Looper.clearMainLooperForTest(); @@ -55,4 +91,19 @@ public class RavenwoodRuleImpl { android.os.Binder.reset$ravenwood(); android.os.Process.reset$ravenwood(); } + + private static void dumpStacks() { + final PrintStream out = System.err; + out.println("-----BEGIN ALL THREAD STACKS-----"); + final Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces(); + for (Map.Entry<Thread, StackTraceElement[]> stack : stacks.entrySet()) { + out.println(); + Thread t = stack.getKey(); + out.println(t.toString() + " ID=" + t.getId()); + for (StackTraceElement e : stack.getValue()) { + out.println("\tat " + e); + } + } + out.println("-----END ALL THREAD STACKS-----"); + } } diff --git a/ravenwood/junit-src/android/platform/test/annotations/IncludeUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java index 0b2e32f67960..4bf09807c360 100644 --- a/ravenwood/junit-src/android/platform/test/annotations/IncludeUnderRavenwood.java +++ b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnRavenwood.java @@ -23,15 +23,16 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Tests marked with this annotation are included when running under a Ravenwood test environment. + * Tests marked with this annotation are disabled when running under a Ravenwood test environment. * * A more specific method-level annotation always takes precedence over any class-level - * annotation, and an {@link IncludeUnderRavenwood} annotation always takes precedence over - * an {@link ExcludeUnderRavenwood} annotation. + * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over + * an {@link DisabledOnRavenwood} annotation. * * This annotation only takes effect when the containing class has a {@code - * RavenwoodRule} configured. Ignoring is accomplished by throwing an {@code org.junit - * .AssumptionViolatedException} which test infrastructure treats as being ignored. + * RavenwoodRule} or {@code RavenwoodClassRule} configured. Ignoring is accomplished by + * throwing an {@code org.junit.AssumptionViolatedException} which test infrastructure treats as + * being ignored. * * This annotation has no effect on any other non-Ravenwood test environments. * @@ -40,5 +41,5 @@ import java.lang.annotation.Target; @Inherited @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -public @interface IncludeUnderRavenwood { +public @interface DisabledOnRavenwood { } diff --git a/ravenwood/junit-src/android/platform/test/annotations/ExcludeUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/EnabledOnRavenwood.java index 2ef381e8682c..9dd0a58a9db1 100644 --- a/ravenwood/junit-src/android/platform/test/annotations/ExcludeUnderRavenwood.java +++ b/ravenwood/junit-src/android/platform/test/annotations/EnabledOnRavenwood.java @@ -23,15 +23,16 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Tests marked with this annotation are excluded when running under a Ravenwood test environment. + * Tests marked with this annotation are enabled when running under a Ravenwood test environment. * * A more specific method-level annotation always takes precedence over any class-level - * annotation, and an {@link IncludeUnderRavenwood} annotation always takes precedence over - * an {@link ExcludeUnderRavenwood} annotation. + * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over + * an {@link DisabledOnRavenwood} annotation. * * This annotation only takes effect when the containing class has a {@code - * RavenwoodRule} configured. Ignoring is accomplished by throwing an {@code org.junit - * .AssumptionViolatedException} which test infrastructure treats as being ignored. + * RavenwoodRule} or {@code RavenwoodClassRule} configured. Ignoring is accomplished by + * throwing an {@code org.junit.AssumptionViolatedException} which test infrastructure treats as + * being ignored. * * This annotation has no effect on any other non-Ravenwood test environments. * @@ -40,5 +41,5 @@ import java.lang.annotation.Target; @Inherited @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -public @interface ExcludeUnderRavenwood { +public @interface EnabledOnRavenwood { } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java new file mode 100644 index 000000000000..8d76970f9ad4 --- /dev/null +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.platform.test.ravenwood; + +import static android.platform.test.ravenwood.RavenwoodRule.ENABLE_PROBE_IGNORED; +import static android.platform.test.ravenwood.RavenwoodRule.IS_ON_RAVENWOOD; +import static android.platform.test.ravenwood.RavenwoodRule.shouldEnableOnRavenwood; +import static android.platform.test.ravenwood.RavenwoodRule.shouldStillIgnoreInProbeIgnoreMode; + +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.annotations.EnabledOnRavenwood; + +import org.junit.Assume; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * {@code @ClassRule} that respects Ravenwood-specific class annotations. This rule has no effect + * when tests are run on non-Ravenwood test environments. + * + * By default, all tests are executed on Ravenwood, but annotations such as + * {@link DisabledOnRavenwood} and {@link EnabledOnRavenwood} can be used at both the method + * and class level to "ignore" tests that may not be ready. + */ +public class RavenwoodClassRule implements TestRule { + @Override + public Statement apply(Statement base, Description description) { + // No special treatment when running outside Ravenwood; run tests as-is + if (!IS_ON_RAVENWOOD) { + return base; + } + + if (ENABLE_PROBE_IGNORED) { + Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description)); + // Pass through to possible underlying RavenwoodRule for both environment + // configuration and handling method-level annotations + return base; + } else { + return new Statement() { + @Override + public void evaluate() throws Throwable { + Assume.assumeTrue(shouldEnableOnRavenwood(description)); + // Pass through to possible underlying RavenwoodRule for both environment + // configuration and handling method-level annotations + base.evaluate(); + } + }; + } + } +} diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index dd442f08321f..1e7cbf6d802d 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -18,41 +18,88 @@ package android.platform.test.ravenwood; import static org.junit.Assert.fail; -import android.platform.test.annotations.ExcludeUnderRavenwood; +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.annotations.EnabledOnRavenwood; import android.platform.test.annotations.IgnoreUnderRavenwood; -import android.platform.test.annotations.IncludeUnderRavenwood; import org.junit.Assume; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; /** - * THIS RULE IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY - * QUESTIONS ABOUT IT. + * {@code @Rule} that configures the Ravenwood test environment. This rule has no effect when + * tests are run on non-Ravenwood test environments. * - * @hide + * This rule initializes and resets the Ravenwood environment between each test method to offer a + * hermetic testing environment. + * + * By default, all tests are executed on Ravenwood, but annotations such as + * {@link DisabledOnRavenwood} and {@link EnabledOnRavenwood} can be used at both the method + * and class level to "ignore" tests that may not be ready. When needed, a + * {@link RavenwoodClassRule} can be used in addition to a {@link RavenwoodRule} to ignore tests + * before a test class is fully initialized. */ public class RavenwoodRule implements TestRule { - private static AtomicInteger sNextPid = new AtomicInteger(100); - - private static final boolean IS_UNDER_RAVENWOOD = RavenwoodRuleImpl.isUnderRavenwood(); + static final boolean IS_ON_RAVENWOOD = RavenwoodRuleImpl.isOnRavenwood(); /** - * When probing is enabled, all tests will be unconditionally run under Ravenwood to detect + * When probing is enabled, all tests will be unconditionally run on Ravenwood to detect * cases where a test is able to pass despite being marked as {@code IgnoreUnderRavenwood}. * * This is typically helpful for internal maintainers discovering tests that had previously * been ignored, but now have enough Ravenwood-supported functionality to be enabled. */ - private static final boolean ENABLE_PROBE_IGNORED = false; // DO NOT SUBMIT WITH TRUE + static final boolean ENABLE_PROBE_IGNORED = "1".equals( + System.getenv("RAVENWOOD_RUN_DISABLED_TESTS")); + + /** + * When using ENABLE_PROBE_IGNORED, you may still want to skip certain tests, + * for example because the test would crash the JVM. + * + * This regex defines the tests that should still be disabled even if ENABLE_PROBE_IGNORED + * is set. + * + * Before running each test class and method, we check if this pattern can be found in + * the full test name (either [class full name], or [class full name] + "#" + [method name]), + * and if so, we skip it. + * + * For example, if you want to skip an entire test class, use: + * RAVENWOOD_REALLY_DISABLE='\.CustomTileDefaultsRepositoryTest$' + * + * For example, if you want to skip an entire test class, use: + * RAVENWOOD_REALLY_DISABLE='\.CustomTileDefaultsRepositoryTest#testSimple$' + * + * To ignore multiple classes, use (...|...), for example: + * RAVENWOOD_REALLY_DISABLE='\.(ClassA|ClassB)$' + * + * Because we use a regex-find, setting "." would disable all tests. + */ + private static final Pattern REALLY_DISABLE_PATTERN = Pattern.compile( + Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLE"), "")); + + private static final boolean ENABLE_REALLY_DISABLE_PATTERN = + !REALLY_DISABLE_PATTERN.pattern().isEmpty(); + + static { + if (ENABLE_PROBE_IGNORED) { + System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests"); + if (ENABLE_REALLY_DISABLE_PATTERN) { + System.out.println("$RAVENWOOD_REALLY_DISABLE=" + REALLY_DISABLE_PATTERN.pattern()); + } + } + } private static final int SYSTEM_UID = 1000; private static final int NOBODY_UID = 9999; private static final int FIRST_APPLICATION_UID = 10000; + private static final AtomicInteger sNextPid = new AtomicInteger(100); + /** * Unless the test author requests differently, run as "nobody", and give each collection of * tests its own unique PID. @@ -75,7 +122,7 @@ public class RavenwoodRule implements TestRule { /** * Configure the identity of this process to be the system UID for the duration of the - * test. Has no effect under non-Ravenwood environments. + * test. Has no effect on non-Ravenwood environments. */ public Builder setProcessSystem() { mRule.mUid = SYSTEM_UID; @@ -84,7 +131,7 @@ public class RavenwoodRule implements TestRule { /** * Configure the identity of this process to be an app UID for the duration of the - * test. Has no effect under non-Ravenwood environments. + * test. Has no effect on non-Ravenwood environments. */ public Builder setProcessApp() { mRule.mUid = FIRST_APPLICATION_UID; @@ -93,7 +140,7 @@ public class RavenwoodRule implements TestRule { /** * Configure a "main" thread to be available for the duration of the test, as defined - * by {@code Looper.getMainLooper()}. Has no effect under non-Ravenwood environments. + * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments. */ public Builder setProvideMainThread(boolean provideMainThread) { mRule.mProvideMainThread = provideMainThread; @@ -108,7 +155,7 @@ public class RavenwoodRule implements TestRule { * All properties in the {@code debug.*} namespace are automatically mutable, with no * developer action required. * - * Has no effect under non-Ravenwood environments. + * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyImmutable(/* @NonNull */ String key, /* @Nullable */ Object value) { @@ -125,7 +172,7 @@ public class RavenwoodRule implements TestRule { * All properties in the {@code debug.*} namespace are automatically mutable, with no * developer action required. * - * Has no effect under non-Ravenwood environments. + * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyMutable(/* @NonNull */ String key, /* @Nullable */ Object value) { @@ -140,42 +187,51 @@ public class RavenwoodRule implements TestRule { } /** - * Return if the current process is running under a Ravenwood test environment. + * @deprecated replaced by {@link #isOnRavenwood()} */ + @Deprecated public static boolean isUnderRavenwood() { - return IS_UNDER_RAVENWOOD; + return IS_ON_RAVENWOOD; + } + + /** + * Return if the current process is running on a Ravenwood test environment. + */ + public static boolean isOnRavenwood() { + return IS_ON_RAVENWOOD; } /** - * Determine if the given {@link Description} should be included when running under the + * Determine if the given {@link Description} should be enabled when running on the * Ravenwood test environment. * * A more specific method-level annotation always takes precedence over any class-level - * annotation, and an {@link IncludeUnderRavenwood} annotation always takes precedence over - * an {@link ExcludeUnderRavenwood} annotation. + * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over + * an {@link DisabledOnRavenwood} annotation. */ - private boolean shouldIncludeUnderRavenwood(Description description) { - // Stopgap for http://g/ravenwood/EPAD-N5ntxM - if (description.getMethodName().endsWith("$noRavenwood")) { - return false; - } - + static boolean shouldEnableOnRavenwood(Description description) { // First, consult any method-level annotations - if (description.getAnnotation(IncludeUnderRavenwood.class) != null) { - return true; - } - if (description.getAnnotation(ExcludeUnderRavenwood.class) != null) { - return false; - } - if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { - return false; + if (description.isTest()) { + // Stopgap for http://g/ravenwood/EPAD-N5ntxM + if (description.getMethodName().endsWith("$noRavenwood")) { + return false; + } + if (description.getAnnotation(EnabledOnRavenwood.class) != null) { + return true; + } + if (description.getAnnotation(DisabledOnRavenwood.class) != null) { + return false; + } + if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { + return false; + } } // Otherwise, consult any class-level annotations - if (description.getTestClass().getAnnotation(IncludeUnderRavenwood.class) != null) { + if (description.getTestClass().getAnnotation(EnabledOnRavenwood.class) != null) { return true; } - if (description.getTestClass().getAnnotation(ExcludeUnderRavenwood.class) != null) { + if (description.getTestClass().getAnnotation(DisabledOnRavenwood.class) != null) { return false; } if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { @@ -186,10 +242,25 @@ public class RavenwoodRule implements TestRule { return true; } + static boolean shouldStillIgnoreInProbeIgnoreMode(Description description) { + if (!ENABLE_REALLY_DISABLE_PATTERN) { + return false; + } + + final var fullname = description.getTestClass().getName() + + (description.isTest() ? "#" + description.getMethodName() : ""); + + if (REALLY_DISABLE_PATTERN.matcher(fullname).find()) { + System.out.println("Still ignoring " + fullname); + return true; + } + return false; + } + @Override public Statement apply(Statement base, Description description) { // No special treatment when running outside Ravenwood; run tests as-is - if (!IS_UNDER_RAVENWOOD) { + if (!IS_ON_RAVENWOOD) { return base; } @@ -207,7 +278,7 @@ public class RavenwoodRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { - Assume.assumeTrue(shouldIncludeUnderRavenwood(description)); + Assume.assumeTrue(shouldEnableOnRavenwood(description)); RavenwoodRuleImpl.init(RavenwoodRule.this); try { @@ -221,26 +292,27 @@ public class RavenwoodRule implements TestRule { /** * Run the given {@link Statement} with probing enabled. All tests will be unconditionally - * run under Ravenwood to detect cases where a test is able to pass despite being marked as + * run on Ravenwood to detect cases where a test is able to pass despite being marked as * {@code IgnoreUnderRavenwood}. */ private Statement applyProbeIgnored(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { + Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description)); RavenwoodRuleImpl.init(RavenwoodRule.this); try { base.evaluate(); } catch (Throwable t) { // If the test isn't included, eat the exception and report the // assumption failure that test authors expect; otherwise throw - Assume.assumeTrue(shouldIncludeUnderRavenwood(description)); + Assume.assumeTrue(shouldEnableOnRavenwood(description)); throw t; } finally { RavenwoodRuleImpl.reset(RavenwoodRule.this); } - if (!shouldIncludeUnderRavenwood(description)) { + if (!shouldEnableOnRavenwood(description)) { fail("Test wasn't included under Ravenwood, but it actually " + "passed under Ravenwood; consider updating annotations"); } diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index 0ff6a1ad846b..d0c2e18be8df 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -17,7 +17,7 @@ package android.platform.test.ravenwood; public class RavenwoodRuleImpl { - public static boolean isUnderRavenwood() { + public static boolean isOnRavenwood() { return false; } diff --git a/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java index 36fa3dd94e29..364a86a0ec5f 100644 --- a/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java +++ b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; -import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import org.junit.Rule; @@ -62,31 +61,7 @@ public class RavenwoodMockitoTest { assertThat(object.exitValue()).isEqualTo(42); } - /* - - Intent can't be mocked because of the dependency to `org.xmlpull.v1.XmlPullParser`. - (The error says "Mockito can only mock non-private & non-final classes", but that's likely a - red-herring.) - -STACKTRACE: -org.mockito.exceptions.base.MockitoException: -Mockito cannot mock this class: class android.content.Intent. - - : - -Underlying exception : java.lang.IllegalArgumentException: Could not create type - at com.android.ravenwood.mockito.RavenwoodMockitoTest.testMockAndroidClass1 - at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - - : - -Caused by: java.lang.ClassNotFoundException: org.xmlpull.v1.XmlPullParser - at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) - at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) - at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) - ... 54 more - */ @Test - @IgnoreUnderRavenwood public void testMockAndroidClass1() { Intent object = mock(Intent.class); diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index eaf01a32592e..b775f9ad47ad 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -72,6 +72,7 @@ android.os.Process android.os.ServiceSpecificException android.os.SystemClock android.os.SystemProperties +android.os.TestLooperManager android.os.ThreadLocalWorkSource android.os.TimestampedValue android.os.Trace @@ -141,6 +142,8 @@ android.graphics.RectF android.content.ContentProvider +android.app.Instrumentation + android.metrics.LogMaker android.view.Display$HdrCapabilities diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d656892062d1..63784ba61150 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -16,6 +16,7 @@ package com.android.server.accessibility; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT; @@ -30,10 +31,7 @@ import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVI import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID; import static android.content.Context.DEVICE_ID_DEFAULT; import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static android.view.accessibility.AccessibilityManager.FlashNotificationReason; -import static android.view.accessibility.AccessibilityManager.ShortcutType; import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; @@ -142,6 +140,7 @@ import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkFeatureInfo; import com.android.internal.accessibility.AccessibilityShortcutController.LaunchableFrameworkFeatureInfo; +import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity; import com.android.internal.accessibility.util.AccessibilityUtils; @@ -1721,7 +1720,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::performAccessibilityShortcutInternal, this, - displayId, ACCESSIBILITY_BUTTON, targetName)); + displayId, ShortcutConstants.UserShortcutType.SOFTWARE, targetName)); } /** @@ -2200,11 +2199,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void showAccessibilityTargetsSelection(int displayId, - @ShortcutType int shortcutType) { + @ShortcutConstants.UserShortcutType int shortcutType) { final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); - final String chooserClassName = (shortcutType == ACCESSIBILITY_SHORTCUT_KEY) - ? AccessibilityShortcutChooserActivity.class.getName() - : AccessibilityButtonChooserActivity.class.getName(); + final String chooserClassName = + (shortcutType == ShortcutConstants.UserShortcutType.HARDWARE) + ? AccessibilityShortcutChooserActivity.class.getName() + : AccessibilityButtonChooserActivity.class.getName(); intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(); @@ -3275,7 +3275,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } final Set<String> currentTargets = - userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY); + userState.getShortcutTargetsLocked(ShortcutConstants.UserShortcutType.HARDWARE); if (targetsFromSetting.equals(currentTargets)) { return false; } @@ -3291,7 +3291,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.mUserId, str -> str, targetsFromSetting); final Set<String> currentTargets = - userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); + userState.getShortcutTargetsLocked(ShortcutConstants.UserShortcutType.SOFTWARE); if (targetsFromSetting.equals(currentTargets)) { return false; } @@ -3346,7 +3346,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) { final Set<String> currentTargets = - userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY); + userState.getShortcutTargetsLocked(ShortcutConstants.UserShortcutType.HARDWARE); final int lastSize = currentTargets.size(); if (lastSize == 0) { return; @@ -3531,7 +3531,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } final Set<String> currentTargets = - userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); + userState.getShortcutTargetsLocked(ShortcutConstants.UserShortcutType.SOFTWARE); final int lastSize = currentTargets.size(); if (lastSize == 0) { return; @@ -3571,7 +3571,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } final Set<String> buttonTargets = - userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); + userState.getShortcutTargetsLocked(ShortcutConstants.UserShortcutType.SOFTWARE); int lastSize = buttonTargets.size(); buttonTargets.removeIf(name -> { if (packageName != null && name != null && !name.contains(packageName)) { @@ -3608,7 +3608,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub lastSize = buttonTargets.size(); final Set<String> shortcutKeyTargets = - userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY); + userState.getShortcutTargetsLocked(ShortcutConstants.UserShortcutType.HARDWARE); userState.mEnabledServices.forEach(componentName -> { if (packageName != null && componentName != null && !packageName.equals(componentName.getPackageName())) { @@ -3665,16 +3665,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } final ComponentName serviceName = service.getComponentName(); - if (userState.removeShortcutTargetLocked(ACCESSIBILITY_SHORTCUT_KEY, serviceName)) { + if (userState.removeShortcutTargetLocked( + ShortcutConstants.UserShortcutType.HARDWARE, serviceName)) { final Set<String> currentTargets = userState.getShortcutTargetsLocked( - ACCESSIBILITY_SHORTCUT_KEY); + ShortcutConstants.UserShortcutType.HARDWARE); persistColonDelimitedSetToSettingLocked( Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId, currentTargets, str -> str); } - if (userState.removeShortcutTargetLocked(ACCESSIBILITY_BUTTON, serviceName)) { + if (userState.removeShortcutTargetLocked( + ShortcutConstants.UserShortcutType.SOFTWARE, serviceName)) { final Set<String> currentTargets = userState.getShortcutTargetsLocked( - ACCESSIBILITY_BUTTON); + ShortcutConstants.UserShortcutType.SOFTWARE); persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userState.mUserId, currentTargets, str -> str); } @@ -3750,7 +3752,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::performAccessibilityShortcutInternal, this, - Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY, targetName)); + Display.DEFAULT_DISPLAY, ShortcutConstants.UserShortcutType.HARDWARE, targetName)); } /** @@ -3763,7 +3765,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * specified target. */ private void performAccessibilityShortcutInternal(int displayId, - @ShortcutType int shortcutType, @Nullable String targetName) { + @ShortcutConstants.UserShortcutType int shortcutType, @Nullable String targetName) { final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType); if (shortcutTargets.isEmpty()) { Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType); @@ -3814,7 +3816,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean performAccessibilityFrameworkFeature(int displayId, - ComponentName assignedTarget, @ShortcutType int shortcutType) { + ComponentName assignedTarget, @ShortcutConstants.UserShortcutType int shortcutType) { final Map<ComponentName, FrameworkFeatureInfo> frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); if (!frameworkFeatureMap.containsKey(assignedTarget)) { @@ -3863,12 +3865,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * Perform accessibility service shortcut action. * - * 1) For {@link AccessibilityManager#ACCESSIBILITY_BUTTON} type and services targeting sdk + * 1) For {@link ShortcutConstants.UserShortcutType.SOFTWARE} type and services targeting sdk * version <= Q: callbacks to accessibility service if service is bounded and requests * accessibility button. - * 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk + * 2) For {@link ShortcutConstants.UserShortcutType.HARDWARE} type and service targeting sdk * version <= Q: turns on / off the accessibility service. - * 3) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk + * 3) For {@link ShortcutConstants.UserShortcutType.HARDWARE} type and service targeting sdk * version > Q and request accessibility button: turn on the accessibility service if it's * not in the enabled state. * (It'll happen when a service is disabled and assigned to shortcut then upgraded.) @@ -3879,7 +3881,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * button. */ private boolean performAccessibilityShortcutTargetService(int displayId, - @ShortcutType int shortcutType, ComponentName assignedTarget) { + @ShortcutConstants.UserShortcutType int shortcutType, ComponentName assignedTarget) { synchronized (mLock) { final AccessibilityUserState userState = getCurrentUserStateLocked(); final AccessibilityServiceInfo installedServiceInfo = @@ -3897,7 +3899,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final boolean requestA11yButton = (installedServiceInfo.flags & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; // Turns on / off the accessibility service - if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY) + if ((targetSdk <= Build.VERSION_CODES.Q + && shortcutType == ShortcutConstants.UserShortcutType.HARDWARE) || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) { if (serviceConnection == null) { logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType, @@ -3911,7 +3914,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } return true; } - if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY && targetSdk > Build.VERSION_CODES.Q + if (shortcutType == ShortcutConstants.UserShortcutType.HARDWARE + && targetSdk > Build.VERSION_CODES.Q && requestA11yButton) { if (!userState.getEnabledServicesLocked().contains(assignedTarget)) { enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId); @@ -3941,7 +3945,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override - public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) { + public List<String> getAccessibilityShortcutTargets( + @ShortcutConstants.UserShortcutType int shortcutType) { if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".getAccessibilityShortcutTargets", FLAGS_ACCESSIBILITY_MANAGER, "shortcutType=" + shortcutType); @@ -3955,12 +3960,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return getAccessibilityShortcutTargetsInternal(shortcutType); } - private List<String> getAccessibilityShortcutTargetsInternal(@ShortcutType int shortcutType) { + private List<String> getAccessibilityShortcutTargetsInternal( + @ShortcutConstants.UserShortcutType int shortcutType) { synchronized (mLock) { final AccessibilityUserState userState = getCurrentUserStateLocked(); final ArrayList<String> shortcutTargets = new ArrayList<>( userState.getShortcutTargetsLocked(shortcutType)); - if (shortcutType != ACCESSIBILITY_BUTTON) { + if (shortcutType != ShortcutConstants.UserShortcutType.SOFTWARE) { return shortcutTargets; } // Adds legacy a11y services requesting a11y button into the list. @@ -4423,7 +4429,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } // Warning is not required if the service is already assigned to a shortcut. - for (int shortcutType : AccessibilityManager.SHORTCUT_TYPES) { + for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) { if (getAccessibilityShortcutTargets(shortcutType).contains( componentName.flattenToString())) { return false; @@ -4603,8 +4609,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { - new AccessibilityShellCommand(this, mSystemActionPerformer).exec(this, in, out, err, args, - callback, resultReceiver); + new AccessibilityShellCommand(this, mSystemActionPerformer) + .exec(this, in, out, err, args, callback, resultReceiver); } private final class InteractionBridge { @@ -5719,6 +5725,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override + public void attachAccessibilityOverlayToDisplay_enforcePermission( + int displayId, SurfaceControl sc) { + mContext.enforceCallingPermission( + INTERNAL_SYSTEM_WINDOW, "attachAccessibilityOverlayToDisplay_enforcePermission"); + mMainHandler.sendMessage( + obtainMessage( + AccessibilityManagerService::attachAccessibilityOverlayToDisplayInternal, + this, + -1, + displayId, + sc, + null)); + } + + @Override public void attachAccessibilityOverlayToDisplay( int interactionId, int displayId, @@ -5754,12 +5775,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub t.close(); result = AccessibilityService.OVERLAY_RESULT_SUCCESS; } - // Send the result back to the service. - try { - callback.sendAttachOverlayResult(result, interactionId); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Exception while attaching overlay.", re); - // the other side will time out + + if (callback != null) { + // Send the result back to the service. + try { + callback.sendAttachOverlayResult(result, interactionId); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Exception while attaching overlay.", re); + // the other side will time out + } } } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 68ee78076f3d..41165b6aed5a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -24,9 +24,6 @@ import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; -import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; -import static android.view.accessibility.AccessibilityManager.ShortcutType; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; @@ -51,6 +48,7 @@ import android.view.accessibility.IAccessibilityManagerClient; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.accessibility.common.ShortcutConstants; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -757,13 +755,21 @@ class AccessibilityUserState { * @param shortcutType The shortcut type. * @return The array set of the strings */ - public ArraySet<String> getShortcutTargetsLocked(@ShortcutType int shortcutType) { - if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY) { + public ArraySet<String> getShortcutTargetsLocked( + @ShortcutConstants.UserShortcutType int shortcutType) { + if (shortcutType == ShortcutConstants.UserShortcutType.HARDWARE) { return mAccessibilityShortcutKeyTargets; - } else if (shortcutType == ACCESSIBILITY_BUTTON) { + } else if (shortcutType == ShortcutConstants.UserShortcutType.SOFTWARE) { return mAccessibilityButtonTargets; + } else if ((shortcutType == ShortcutConstants.UserShortcutType.TRIPLETAP + && isMagnificationSingleFingerTripleTapEnabledLocked()) || ( + shortcutType == ShortcutConstants.UserShortcutType.TWO_FINGERS_TRIPLE_TAP + && isMagnificationTwoFingerTripleTapEnabledLocked())) { + ArraySet<String> targets = new ArraySet<>(); + targets.add(MAGNIFICATION_CONTROLLER_NAME); + return targets; } - return null; + return new ArraySet<>(); } /** @@ -802,12 +808,22 @@ class AccessibilityUserState { /** * Removes given shortcut target in the list. * - * @param shortcutType The shortcut type. - * @param target The component name of the shortcut target. + * @param shortcutType one of {@link ShortcutConstants.UserShortcutType.HARDWARE} or + * {@link ShortcutConstants.UserShortcutType.SOFTWARE}. Other types are not + * implemented. + * @param target The component name of the shortcut target. * @return true if the shortcut target is removed. */ - public boolean removeShortcutTargetLocked(@ShortcutType int shortcutType, + public boolean removeShortcutTargetLocked(@ShortcutConstants.UserShortcutType int shortcutType, ComponentName target) { + if (shortcutType == ShortcutConstants.UserShortcutType.TRIPLETAP + || shortcutType == ShortcutConstants.UserShortcutType.TWO_FINGERS_TRIPLE_TAP) { + throw new UnsupportedOperationException( + "removeShortcutTargetLocked only support shortcut type: " + + "software and hardware for now" + ); + } + return getShortcutTargetsLocked(shortcutType).removeIf(name -> { ComponentName componentName; if (name == null diff --git a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java b/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java index 3482863032f9..aac628cab403 100644 --- a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java +++ b/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java @@ -36,7 +36,8 @@ import com.android.server.LocalServices; * will be killed if association/role are revoked. */ public class InactiveAssociationsRemovalService extends JobService { - private static final int JOB_ID = InactiveAssociationsRemovalService.class.hashCode(); + private static final String JOB_NAMESPACE = "companion"; + private static final int JOB_ID = 1; private static final long ONE_DAY_INTERVAL = DAYS.toMillis(1); @Override @@ -61,7 +62,8 @@ public class InactiveAssociationsRemovalService extends JobService { static void schedule(Context context) { Slog.i(TAG, "Scheduling the Association Removal job"); - final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); + final JobScheduler jobScheduler = + context.getSystemService(JobScheduler.class).forNamespace(JOB_NAMESPACE); final JobInfo job = new JobInfo.Builder(JOB_ID, new ComponentName(context, InactiveAssociationsRemovalService.class)) .setRequiresCharging(true) @@ -71,4 +73,3 @@ public class InactiveAssociationsRemovalService extends JobService { jobScheduler.schedule(job); } } - diff --git a/services/core/Android.bp b/services/core/Android.bp index a54a48a7e84e..8e35b7455d02 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -139,6 +139,7 @@ java_library_static { libs: [ "services.net", + "android.frameworks.location.altitude-V2-java", "android.hardware.common-V2-java", "android.hardware.light-V2.0-java", "android.hardware.gnss-V2-java", @@ -160,7 +161,6 @@ java_library_static { ], static_libs: [ - "android.frameworks.location.altitude-V2-java", // AIDL "android.frameworks.vibrator-V1-java", // AIDL "android.hardware.authsecret-V1.0-java", "android.hardware.authsecret-V1-java", diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index b87184aa5582..416b36feb94d 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -288,22 +288,23 @@ public class BinderCallsStatsService extends Binder { CachedDeviceState.Readonly.class); mBinderCallsStats.setDeviceState(deviceState); - BatteryStatsInternal batteryStatsInternal = getLocalService( - BatteryStatsInternal.class); - mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() { - @Override - public void noteCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats) { - batteryStatsInternal.noteBinderCallStats(workSourceUid, - incrementalCallCount, callStats); - } - - @Override - public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { - batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids); - } - }); - + if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { + BatteryStatsInternal batteryStatsInternal = getLocalService( + BatteryStatsInternal.class); + mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() { + @Override + public void noteCallStats(int workSourceUid, long incrementalCallCount, + Collection<BinderCallsStats.CallStat> callStats) { + batteryStatsInternal.noteBinderCallStats(workSourceUid, + incrementalCallCount, callStats); + } + + @Override + public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { + batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids); + } + }); + } // It needs to be called before mService.systemReady to make sure the observer is // initialized before installing it. mWorkSourceProvider.systemReady(getContext()); diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java index c3916422159e..f619ca3f66a2 100644 --- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java @@ -27,6 +27,7 @@ import android.media.projection.MediaProjectionManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.os.Trace; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; @@ -49,12 +50,12 @@ public final class SensitiveContentProtectionManagerService extends SystemServic private static final String TAG = "SensitiveContentProtect"; private static final boolean DEBUG = false; - @VisibleForTesting - NotificationListener mNotificationListener; + @VisibleForTesting NotificationListener mNotificationListener; private @Nullable MediaProjectionManager mProjectionManager; private @Nullable WindowManagerInternal mWindowManager; final Object mSensitiveContentProtectionLock = new Object(); + @GuardedBy("mSensitiveContentProtectionLock") private boolean mProjectionActive = false; @@ -63,13 +64,24 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @Override public void onStart(MediaProjectionInfo info) { if (DEBUG) Log.d(TAG, "onStart projection: " + info); - onProjectionStart(); + Trace.beginSection( + "SensitiveContentProtectionManagerService.onProjectionStart"); + try { + onProjectionStart(); + } finally { + Trace.endSection(); + } } @Override public void onStop(MediaProjectionInfo info) { if (DEBUG) Log.d(TAG, "onStop projection: " + info); - onProjectionEnd(); + Trace.beginSection("SensitiveContentProtectionManagerService.onProjectionStop"); + try { + onProjectionEnd(); + } finally { + Trace.endSection(); + } } }; @@ -94,8 +106,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } @VisibleForTesting - void init(MediaProjectionManager projectionManager, - WindowManagerInternal windowManager) { + void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager) { if (DEBUG) Log.d(TAG, "init"); checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager"); @@ -109,7 +120,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic mProjectionManager.addCallback(mProjectionCallback, new Handler(Looper.getMainLooper())); try { - mNotificationListener.registerAsSystemService(getContext(), + mNotificationListener.registerAsSystemService( + getContext(), new ComponentName(getContext(), NotificationListener.class), UserHandle.USER_ALL); } catch (RemoteException e) { @@ -174,8 +186,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } // notify windowmanager of any currently posted sensitive content notifications - ArraySet<PackageInfo> packageInfos = getSensitivePackagesFromNotifications( - notifications, rankingMap); + ArraySet<PackageInfo> packageInfos = + getSensitivePackagesFromNotifications(notifications, rankingMap); mWindowManager.addBlockScreenCaptureForApps(packageInfos); } @@ -197,8 +209,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic return sensitivePackages; } - private PackageInfo getSensitivePackageFromNotification(StatusBarNotification sbn, - RankingMap rankingMap) { + private PackageInfo getSensitivePackageFromNotification( + StatusBarNotification sbn, RankingMap rankingMap) { if (sbn == null) { Log.w(TAG, "Unable to protect null notification"); return null; @@ -220,38 +232,55 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @Override public void onListenerConnected() { super.onListenerConnected(); - // Projection started before notification listener was connected - synchronized (mSensitiveContentProtectionLock) { - if (mProjectionActive) { - updateAppsThatShouldBlockScreenCapture(); + Trace.beginSection("SensitiveContentProtectionManagerService.onListenerConnected"); + try { + // Projection started before notification listener was connected + synchronized (mSensitiveContentProtectionLock) { + if (mProjectionActive) { + updateAppsThatShouldBlockScreenCapture(); + } } + } finally { + Trace.endSection(); } } @Override public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { super.onNotificationPosted(sbn, rankingMap); - synchronized (mSensitiveContentProtectionLock) { - if (!mProjectionActive) { - return; - } - - // notify windowmanager of any currently posted sensitive content notifications - PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap); - - if (packageInfo != null) { - mWindowManager.addBlockScreenCaptureForApps(new ArraySet(Set.of(packageInfo))); + Trace.beginSection("SensitiveContentProtectionManagerService.onNotificationPosted"); + try { + synchronized (mSensitiveContentProtectionLock) { + if (!mProjectionActive) { + return; + } + + // notify windowmanager of any currently posted sensitive content notifications + PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap); + + if (packageInfo != null) { + mWindowManager.addBlockScreenCaptureForApps( + new ArraySet(Set.of(packageInfo))); + } } + } finally { + Trace.endSection(); } } @Override public void onNotificationRankingUpdate(RankingMap rankingMap) { super.onNotificationRankingUpdate(rankingMap); - synchronized (mSensitiveContentProtectionLock) { - if (mProjectionActive) { - updateAppsThatShouldBlockScreenCapture(rankingMap); + Trace.beginSection( + "SensitiveContentProtectionManagerService.onNotificationRankingUpdate"); + try { + synchronized (mSensitiveContentProtectionLock) { + if (mProjectionActive) { + updateAppsThatShouldBlockScreenCapture(rankingMap); + } } + } finally { + Trace.endSection(); } } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index afb8345249b1..adc0255743c2 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -181,7 +181,6 @@ import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo.ForegroundServiceType; -import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -306,6 +305,57 @@ public final class ActiveServices { @interface FgsStopReason {} /** + * The policy to be applied to the service bindings; this one means it follows the legacy + * behavior. + */ + static final int SERVICE_BIND_OOMADJ_POLICY_LEGACY = 0; + + /** + * The policy to be applied to the service bindings; this one means we'll skip + * updating the target process's oom adj score / process state for its {@link Service#onCreate}. + */ + static final int SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE = 1; + + /** + * The policy to be applied to the service bindings; this one means we'll skip + * updating the target process's oom adj score / process state for its {@link Service#onBind}. + */ + static final int SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND = 1 << 1; + + /** + * The policy to be applied to the service bindings; this one means we'll skip + * updating the target process's oom adj score / process state on setting up the service + * connection between the client and the service host process. + */ + static final int SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT = 1 << 2; + /** + * The policy to be applied to the service bindings; this one means the caller + * will be frozen upon calling the bindService APIs. + */ + static final int SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER = 1 << 3; + + @IntDef(flag = true, prefix = { "SERVICE_BIND_OOMADJ_POLICY_" }, value = { + SERVICE_BIND_OOMADJ_POLICY_LEGACY, + SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE, + SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND, + SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT, + SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER, + }) + @Retention(RetentionPolicy.SOURCE) + @interface ServiceBindingOomAdjPolicy {} + + @ServiceBindingOomAdjPolicy + static final int DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG = + SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE + | SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND + | SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT; + + @ServiceBindingOomAdjPolicy + static final int DEFAULT_SERVICE_CACHED_BIND_POLICY_FLAG = + SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE + | SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND; + + /** * Disables foreground service background starts from BOOT_COMPLETED broadcasts for all types * except: * <ul> @@ -1244,7 +1294,7 @@ public final class ActiveServices { @Override public void onResult(Bundle result) { synchronized (mAm) { - final long identity = Binder.clearCallingIdentity(); + final long identity = mAm.mInjector.clearCallingIdentity(); try { if (!mPendingServices.contains(r)) { return; @@ -1263,7 +1313,8 @@ public final class ActiveServices { false /* whileRestarting */, false /* permissionsReviewRequired */, false /* packageFrozen */, - true /* enqueueOomAdj */); + true /* enqueueOomAdj */, + SERVICE_BIND_OOMADJ_POLICY_LEGACY); } catch (RemoteException e) { /* ignore - local call */ } finally { @@ -1275,7 +1326,7 @@ public final class ActiveServices { unbindServiceLocked(connection); } } finally { - Binder.restoreCallingIdentity(identity); + mAm.mInjector.restoreCallingIdentity(identity); } } } @@ -1353,7 +1404,8 @@ public final class ActiveServices { false /* whileRestarting */, false /* permissionsReviewRequired */, false /* packageFrozen */, - true /* enqueueOomAdj */); + true /* enqueueOomAdj */, + SERVICE_BIND_OOMADJ_POLICY_LEGACY); } catch (TransactionTooLargeException e) { /* ignore - local call */ } finally { @@ -1431,7 +1483,8 @@ public final class ActiveServices { false /* whileRestarting */, false /* permissionsReviewRequired */, false /* packageFrozen */, - true /* enqueueOomAdj */); + true /* enqueueOomAdj */, + SERVICE_BIND_OOMADJ_POLICY_LEGACY); /* Will be a no-op if nothing pending */ mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE); if (error != null) { @@ -1550,22 +1603,22 @@ public final class ActiveServices { if (caller != null && callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() + + " (pid=" + mAm.mInjector.getCallingPid() + ") when stopping service " + service); } // If this service is active, make sure it is stopped. ServiceLookupResult r = retrieveServiceLocked(service, instanceName, isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, null, - Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false, - null, false, false); + mAm.mInjector.getCallingPid(), mAm.mInjector.getCallingUid(), + userId, false, false, false, false, null, false, false); if (r != null) { if (r.record != null) { - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); try { stopServiceLocked(r.record, false); } finally { - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); } return 1; } @@ -1649,7 +1702,7 @@ public final class ActiveServices { IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) { ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, callingPackage, - Binder.getCallingPid(), Binder.getCallingUid(), + mAm.mInjector.getCallingPid(), mAm.mInjector.getCallingUid(), UserHandle.getCallingUserId(), false, false, false, false, false, false); IBinder ret = null; @@ -1658,8 +1711,8 @@ public final class ActiveServices { if (r.record == null) { throw new SecurityException( "Permission Denial: Accessing service" - + " from pid=" + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() + + " from pid=" + mAm.mInjector.getCallingPid() + + ", uid=" + mAm.mInjector.getCallingUid() + " requires " + r.permission); } IntentBindRecord ib = r.record.bindings.get(r.record.intent); @@ -1719,9 +1772,9 @@ public final class ActiveServices { } } r.callStart = false; - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); bringDownServiceIfNeededLocked(r, false, false, false, "stopServiceToken"); - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); return true; } return false; @@ -1734,14 +1787,14 @@ public final class ActiveServices { public void setServiceForegroundLocked(ComponentName className, IBinder token, int id, Notification notification, int flags, int foregroundServiceType) { final int userId = UserHandle.getCallingUserId(); - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); try { ServiceRecord r = findServiceLocked(className, token, userId); if (r != null) { setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType); } } finally { - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); } } @@ -1753,7 +1806,7 @@ public final class ActiveServices { */ public int getForegroundServiceTypeLocked(ComponentName className, IBinder token) { final int userId = UserHandle.getCallingUserId(); - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); int ret = ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; try { ServiceRecord r = findServiceLocked(className, token, userId); @@ -1761,7 +1814,7 @@ public final class ActiveServices { ret = r.foregroundServiceType; } } finally { - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); } return ret; } @@ -3483,7 +3536,7 @@ public final class ActiveServices { boolean shouldServiceTimeOutLocked(ComponentName className, IBinder token) { final int userId = UserHandle.getCallingUserId(); - final long ident = Binder.clearCallingIdentity(); + final long ident = mAm.mInjector.clearCallingIdentity(); try { ServiceRecord sr = findServiceLocked(className, token, userId); if (sr == null) { @@ -3492,7 +3545,7 @@ public final class ActiveServices { final long nowUptime = SystemClock.uptimeMillis(); return sr.shouldTriggerShortFgsTimeout(nowUptime); } finally { - Binder.restoreCallingIdentity(ident); + mAm.mInjector.restoreCallingIdentity(ident); } } @@ -3636,8 +3689,8 @@ public final class ActiveServices { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service + " type=" + resolvedType + " conn=" + connection.asBinder() + " flags=0x" + Long.toHexString(flags)); - final int callingPid = Binder.getCallingPid(); - final int callingUid = Binder.getCallingUid(); + final int callingPid = mAm.mInjector.getCallingPid(); + final int callingUid = mAm.mInjector.getCallingUid(); final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller); if (callerApp == null) { throw new SecurityException( @@ -3778,7 +3831,7 @@ public final class ActiveServices { && !requestStartTargetPermissionsReviewIfNeededLocked(s, callingPackage, null, callingUid, service, callerFg, userId, true, connection); - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); try { if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) { @@ -3859,12 +3912,34 @@ public final class ActiveServices { } clist.add(c); + final boolean isolated = (s.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; + final ProcessRecord hostApp = isolated + ? null + : mAm.getProcessRecordLocked(s.processName, s.appInfo.uid); + final int serviceBindingOomAdjPolicy = hostApp != null + ? getServiceBindingOomAdjPolicyForAddLocked(b.client, hostApp, c) + : SERVICE_BIND_OOMADJ_POLICY_LEGACY; + + final boolean shouldFreezeCaller = !packageFrozen && !permissionsReviewRequired + && (serviceBindingOomAdjPolicy & SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER) != 0 + && callerApp.isFreezable(); + + if (shouldFreezeCaller) { + // Freeze the caller immediately, so the following #onBind/#onConnected will be + // queued up in the app side as they're one way calls. And we'll also hold off + // the service timeout timer until the process is unfrozen. + mAm.mOomAdjuster.updateAppFreezeStateLSP(callerApp, OOM_ADJ_REASON_BIND_SERVICE, + true); + } + boolean needOomAdj = false; if (c.hasFlag(Context.BIND_AUTO_CREATE)) { s.lastActivity = SystemClock.uptimeMillis(); - needOomAdj = true; + needOomAdj = (serviceBindingOomAdjPolicy + & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) == 0; if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, - permissionsReviewRequired, packageFrozen, true) != null) { + permissionsReviewRequired, packageFrozen, true, serviceBindingOomAdjPolicy) + != null) { mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_BIND_SERVICE); return 0; } @@ -3886,8 +3961,11 @@ public final class ActiveServices { || (callerApp.mState.getCurProcState() <= PROCESS_STATE_TOP && c.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)), b.client); - needOomAdj = true; - mAm.enqueueOomAdjTargetLocked(s.app); + if ((serviceBindingOomAdjPolicy + & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) == 0) { + needOomAdj = true; + mAm.enqueueOomAdjTargetLocked(s.app); + } } if (needOomAdj) { mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_BIND_SERVICE); @@ -3937,10 +4015,12 @@ public final class ActiveServices { // and the service had previously asked to be told when // rebound, then do so. if (b.intent.apps.size() == 1 && b.intent.doRebind) { - requestServiceBindingLocked(s, b.intent, callerFg, true); + requestServiceBindingLocked(s, b.intent, callerFg, true, + serviceBindingOomAdjPolicy); } } else if (!b.intent.requested) { - requestServiceBindingLocked(s, b.intent, callerFg, false); + requestServiceBindingLocked(s, b.intent, callerFg, false, + serviceBindingOomAdjPolicy); } maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid); @@ -3948,7 +4028,7 @@ public final class ActiveServices { getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s); } finally { - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); } notifyBindingServiceEventLocked(callerApp, callingPackage); @@ -3982,7 +4062,7 @@ public final class ActiveServices { } void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); try { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r + " " + intent + ": " + service); @@ -4025,10 +4105,11 @@ public final class ActiveServices { } serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false, - OOM_ADJ_REASON_EXECUTING_SERVICE); + !Flags.serviceBindingOomAdjPolicy() || b == null || !b.mSkippedOomAdj + ? OOM_ADJ_REASON_EXECUTING_SERVICE : OOM_ADJ_REASON_NONE); } } finally { - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); } } @@ -4078,8 +4159,8 @@ public final class ActiveServices { return false; } - final int callingPid = Binder.getCallingPid(); - final long origId = Binder.clearCallingIdentity(); + final int callingPid = mAm.mInjector.getCallingPid(); + final long origId = mAm.mInjector.clearCallingIdentity(); try { if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { String info; @@ -4092,9 +4173,10 @@ public final class ActiveServices { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "unbindServiceLocked: " + info); } + boolean needOomAdj = false; while (clist.size() > 0) { ConnectionRecord r = clist.get(0); - removeConnectionLocked(r, null, null, true); + int serviceBindingOomAdjPolicy = removeConnectionLocked(r, null, null, true); if (clist.size() > 0 && clist.get(0) == r) { // In case it didn't get removed above, do it now. Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder); @@ -4112,22 +4194,28 @@ public final class ActiveServices { psr.setTreatLikeActivity(true); mAm.updateLruProcessLocked(app, true, null); } - mAm.enqueueOomAdjTargetLocked(app); + // If the bindee is more important than the binder, we may skip the OomAdjuster. + if (serviceBindingOomAdjPolicy == SERVICE_BIND_OOMADJ_POLICY_LEGACY) { + mAm.enqueueOomAdjTargetLocked(app); + needOomAdj = true; + } } } - mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_UNBIND_SERVICE); + if (needOomAdj) { + mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_UNBIND_SERVICE); + } } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); } return true; } void unbindFinishedLocked(ServiceRecord r, Intent intent, boolean doRebind) { - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); try { if (r != null) { Intent.FilterComparison filter @@ -4138,6 +4226,7 @@ public final class ActiveServices { + (b != null ? b.apps.size() : 0)); boolean inDestroying = mDestroyingServices.contains(r); + boolean skipOomAdj = false; if (b != null) { if (b.apps.size() > 0 && !inDestroying) { // Applications have already bound since the last @@ -4152,7 +4241,8 @@ public final class ActiveServices { } } try { - requestServiceBindingLocked(r, b, inFg, true); + requestServiceBindingLocked(r, b, inFg, true, + SERVICE_BIND_OOMADJ_POLICY_LEGACY); } catch (TransactionTooLargeException e) { // Don't pass this back to ActivityThread, it's unrelated. } @@ -4161,13 +4251,14 @@ public final class ActiveServices { // a new client. b.doRebind = true; } + skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b.mSkippedOomAdj; } serviceDoneExecutingLocked(r, inDestroying, false, false, - OOM_ADJ_REASON_UNBIND_SERVICE); + skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE); } } finally { - Binder.restoreCallingIdentity(origId); + mAm.mInjector.restoreCallingIdentity(origId); } } @@ -4503,7 +4594,7 @@ public final class ActiveServices { userId = 0; smap = getServiceMapLocked(0); // Bypass INTERACT_ACROSS_USERS permission check - final long token = Binder.clearCallingIdentity(); + final long token = mAm.mInjector.clearCallingIdentity(); try { ResolveInfo rInfoForUserId0 = mAm.getPackageManagerInternal().resolveService(service, @@ -4516,7 +4607,7 @@ public final class ActiveServices { } sInfo = rInfoForUserId0.serviceInfo; } finally { - Binder.restoreCallingIdentity(token); + mAm.mInjector.restoreCallingIdentity(token); } } sInfo = new ServiceInfo(sInfo); @@ -4645,7 +4736,8 @@ public final class ActiveServices { * @return {@code true} if it performed oomAdjUpdate. */ private boolean bumpServiceExecutingLocked( - ServiceRecord r, boolean fg, String why, @OomAdjReason int oomAdjReason) { + ServiceRecord r, boolean fg, String why, @OomAdjReason int oomAdjReason, + boolean skipTimeoutIfPossible) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING " + why + " of " + r + " in app " + r.app); else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING " @@ -4669,6 +4761,10 @@ public final class ActiveServices { timeoutNeeded = false; } + // If the process is frozen or to be frozen, and we want to skip the timeout, skip it. + final boolean shouldSkipTimeout = skipTimeoutIfPossible && r.app != null + && (r.app.mOptRecord.isPendingFreeze() || r.app.mOptRecord.isFrozen()); + ProcessServiceRecord psr; if (r.executeNesting == 0) { r.executeFg = fg; @@ -4684,7 +4780,11 @@ public final class ActiveServices { psr.startExecutingService(r); psr.setExecServicesFg(psr.shouldExecServicesFg() || fg); if (timeoutNeeded && psr.numberOfExecutingServices() == 1) { - scheduleServiceTimeoutLocked(r.app); + if (!shouldSkipTimeout) { + scheduleServiceTimeoutLocked(r.app); + } else { + r.app.mServices.noteScheduleServiceTimeoutPending(true); + } } } } else if (r.app != null && fg) { @@ -4692,7 +4792,11 @@ public final class ActiveServices { if (!psr.shouldExecServicesFg()) { psr.setExecServicesFg(true); if (timeoutNeeded) { - scheduleServiceTimeoutLocked(r.app); + if (!shouldSkipTimeout) { + scheduleServiceTimeoutLocked(r.app); + } else { + r.app.mServices.noteScheduleServiceTimeoutPending(true); + } } } } @@ -4712,16 +4816,22 @@ public final class ActiveServices { } private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, - boolean execInFg, boolean rebind) throws TransactionTooLargeException { + boolean execInFg, boolean rebind, + @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy) + throws TransactionTooLargeException { if (r.app == null || r.app.getThread() == null) { // If service is not currently running, can't yet bind. return false; } if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested + " rebind=" + rebind); + final boolean skipOomAdj = (serviceBindingOomAdjPolicy + & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND) != 0; if ((!i.requested || rebind) && i.apps.size() > 0) { try { - bumpServiceExecutingLocked(r, execInFg, "bind", OOM_ADJ_REASON_BIND_SERVICE); + i.mSkippedOomAdj = !bumpServiceExecutingLocked(r, execInFg, "bind", + skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_BIND_SERVICE, + skipOomAdj /* skipTimeoutIfPossible */); if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "requestServiceBinding=" + i.intent.getIntent() + ". bindSeq=" + mBindServiceSeqCounter); @@ -4738,14 +4848,14 @@ public final class ActiveServices { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e); final boolean inDestroying = mDestroyingServices.contains(r); serviceDoneExecutingLocked(r, inDestroying, inDestroying, false, - OOM_ADJ_REASON_UNBIND_SERVICE); + skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE); throw e; } catch (RemoteException e) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r); // Keep the executeNesting count accurate. final boolean inDestroying = mDestroyingServices.contains(r); serviceDoneExecutingLocked(r, inDestroying, inDestroying, false, - OOM_ADJ_REASON_UNBIND_SERVICE); + skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE); return false; } } @@ -5117,7 +5227,7 @@ public final class ActiveServices { } try { bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false, - false, true); + false, true, SERVICE_BIND_OOMADJ_POLICY_LEGACY); } catch (TransactionTooLargeException e) { // Ignore, it's been logged and nothing upstack cares. } finally { @@ -5217,7 +5327,7 @@ public final class ActiveServices { private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, - boolean enqueueOomAdj) + boolean enqueueOomAdj, @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy) throws TransactionTooLargeException { try { if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { @@ -5225,7 +5335,8 @@ public final class ActiveServices { "bringUpServiceLocked: " + r.shortInstanceName); } return bringUpServiceInnerLocked(r, intentFlags, execInFg, whileRestarting, - permissionsReviewRequired, packageFrozen, enqueueOomAdj); + permissionsReviewRequired, packageFrozen, enqueueOomAdj, + serviceBindingOomAdjPolicy); } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -5233,7 +5344,7 @@ public final class ActiveServices { private String bringUpServiceInnerLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, - boolean enqueueOomAdj) + boolean enqueueOomAdj, @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy) throws TransactionTooLargeException { if (r.app != null && r.app.isThreadReady()) { sendServiceArgsLocked(r, execInFg, false); @@ -5317,7 +5428,7 @@ public final class ActiveServices { app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats); realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg, - enqueueOomAdj); + enqueueOomAdj, serviceBindingOomAdjPolicy); return null; } catch (TransactionTooLargeException e) { throw e; @@ -5347,7 +5458,7 @@ public final class ActiveServices { "realStartServiceLocked: " + r.shortInstanceName); } realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg, - enqueueOomAdj); + enqueueOomAdj, SERVICE_BIND_OOMADJ_POLICY_LEGACY); return null; } catch (TransactionTooLargeException e) { throw e; @@ -5452,16 +5563,61 @@ public final class ActiveServices { return HostingRecord.TRIGGER_TYPE_UNKNOWN; } - private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) + private void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg, + @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy) throws TransactionTooLargeException { for (int i=r.bindings.size()-1; i>=0; i--) { IntentBindRecord ibr = r.bindings.valueAt(i); - if (!requestServiceBindingLocked(r, ibr, execInFg, false)) { + if (!requestServiceBindingLocked(r, ibr, execInFg, false, serviceBindingOomAdjPolicy)) { break; } } } + @ServiceBindingOomAdjPolicy + private int getServiceBindingOomAdjPolicyForAddLocked(ProcessRecord clientApp, + ProcessRecord hostApp, ConnectionRecord cr) { + @ServiceBindingOomAdjPolicy int policy = SERVICE_BIND_OOMADJ_POLICY_LEGACY; + if (Flags.serviceBindingOomAdjPolicy() && clientApp != null && hostApp != null) { + if (clientApp == hostApp) { + policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG; + } else if (clientApp.isCached()) { + policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG; + if (clientApp.isFreezable()) { + policy |= SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER; + } + } + if ((policy & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) == 0) { + // Binding between two different processes. + // Check if the caller has a better process state, oom adj score, + // or if the caller has more capabilities. + if (!mAm.mOomAdjuster.evaluateServiceConnectionAdd(clientApp, hostApp, cr)) { + // Running an oom adjuster won't be give the host app a better score, skip it. + policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG; + } + } + } + return policy; + } + + @ServiceBindingOomAdjPolicy + private int getServiceBindingOomAdjPolicyForRemovalLocked(ProcessRecord clientApp, + ProcessRecord hostApp, ConnectionRecord cr) { + @ServiceBindingOomAdjPolicy int policy = SERVICE_BIND_OOMADJ_POLICY_LEGACY; + if (Flags.serviceBindingOomAdjPolicy() && clientApp != null && hostApp != null + && cr != null) { + if (clientApp == hostApp) { + policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG; + } else { + if (!mAm.mOomAdjuster.evaluateServiceConnectionRemoval(clientApp, hostApp, cr)) { + // Running an oom adjuster won't be give the host app a better score, skip it. + policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG; + } + } + } + return policy; + } + /** * Note the name of this method should not be confused with the started services concept. * The "start" here means bring up the instance in the client, and this method is called @@ -5469,7 +5625,8 @@ public final class ActiveServices { */ private void realStartServiceLocked(ServiceRecord r, ProcessRecord app, IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg, - boolean enqueueOomAdj) throws RemoteException { + boolean enqueueOomAdj, @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy) + throws RemoteException { if (thread == null) { throw new RemoteException(); } @@ -5478,17 +5635,28 @@ public final class ActiveServices { + ", ProcessRecord.uid = " + app.uid); r.setProcess(app, thread, pid, uidRecord); r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); - + final boolean skipOomAdj = (serviceBindingOomAdjPolicy + & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) != 0; final ProcessServiceRecord psr = app.mServices; final boolean newService = psr.startService(r); bumpServiceExecutingLocked(r, execInFg, "create", - OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */); + OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */, + skipOomAdj /* skipTimeoutIfPossible */); mAm.updateLruProcessLocked(app, false, null); updateServiceForegroundLocked(psr, /* oomAdj= */ false); - // Force an immediate oomAdjUpdate, so the client app could be in the correct process state - // before doing any service related transactions - mAm.enqueueOomAdjTargetLocked(app); - mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE); + // Skip the oom adj update if it's a self-binding, the Service#onCreate() will be running + // at its current adj score. + if (!skipOomAdj) { + // Force an immediate oomAdjUpdate, so the host app could be in the correct + // process state before doing any service related transactions + mAm.enqueueOomAdjTargetLocked(app); + mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE); + } else { + // Since we skipped the oom adj update, the Service#onCreate() might be running in + // the cached state, if the service process drops into the cached state after the call. + // But there is still a grace period before freezing it, so we should be fine + // in terms of not getting an ANR. + } boolean created = false; try { @@ -5523,7 +5691,7 @@ public final class ActiveServices { // Keep the executeNesting count accurate. final boolean inDestroying = mDestroyingServices.contains(r); serviceDoneExecutingLocked(r, inDestroying, inDestroying, false, - OOM_ADJ_REASON_STOP_SERVICE); + skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_STOP_SERVICE); // Cleanup. if (newService) { @@ -5542,7 +5710,7 @@ public final class ActiveServices { psr.mAllowlistManager = true; } - requestServiceBindingsLocked(r, execInFg); + requestServiceBindingsLocked(r, execInFg, serviceBindingOomAdjPolicy); updateServiceClientActivitiesLocked(psr, null, true); @@ -5610,7 +5778,8 @@ public final class ActiveServices { UserHandle.getAppId(r.appInfo.uid) ); bumpServiceExecutingLocked(r, execInFg, "start", - OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */); + OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */, + false /* skipTimeoutIfPossible */); if (r.fgRequired && !r.fgWaiting) { if (!r.isForeground) { if (DEBUG_BACKGROUND_CHECK) { @@ -5753,7 +5922,8 @@ public final class ActiveServices { if (ibr.hasBound) { try { oomAdjusted |= bumpServiceExecutingLocked(r, false, "bring down unbind", - OOM_ADJ_REASON_UNBIND_SERVICE); + OOM_ADJ_REASON_UNBIND_SERVICE, + false /* skipTimeoutIfPossible */); ibr.hasBound = false; ibr.requested = false; r.app.getThread().scheduleUnbindService(r, @@ -5909,7 +6079,8 @@ public final class ActiveServices { } else { try { oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy", - oomAdjusted ? 0 : OOM_ADJ_REASON_STOP_SERVICE); + oomAdjusted ? 0 : OOM_ADJ_REASON_STOP_SERVICE, + false /* skipTimeoutIfPossible */); mDestroyingServices.add(r); r.destroying = true; r.app.getThread().scheduleStopService(r); @@ -5992,11 +6163,17 @@ public final class ActiveServices { } } - void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp, + /** + * @return The ServiceBindingOomAdjPolicy used in this removal. + */ + @ServiceBindingOomAdjPolicy + int removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp, ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) { IBinder binder = c.conn.asBinder(); AppBindRecord b = c.binding; ServiceRecord s = b.service; + @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy = + SERVICE_BIND_OOMADJ_POLICY_LEGACY; ArrayList<ConnectionRecord> clist = s.getConnections().get(binder); if (clist != null) { clist.remove(c); @@ -6055,8 +6232,14 @@ public final class ActiveServices { + ": shouldUnbind=" + b.intent.hasBound); if (s.app != null && s.app.isThreadReady() && b.intent.apps.size() == 0 && b.intent.hasBound) { + serviceBindingOomAdjPolicy = getServiceBindingOomAdjPolicyForRemovalLocked(b.client, + s.app, c); + final boolean skipOomAdj = (serviceBindingOomAdjPolicy + & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) != 0; try { - bumpServiceExecutingLocked(s, false, "unbind", OOM_ADJ_REASON_UNBIND_SERVICE); + b.intent.mSkippedOomAdj = !bumpServiceExecutingLocked(s, false, "unbind", + skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE, + skipOomAdj /* skipTimeoutIfPossible */); if (b.client != s.app && c.notHasFlag(Context.BIND_WAIVE_PRIORITY) && s.app.mState.getSetProcState() <= PROCESS_STATE_HEAVY_WEIGHT) { // If this service's process is not already in the cached list, @@ -6096,12 +6279,14 @@ public final class ActiveServices { "removeConnection"); } } + return serviceBindingOomAdjPolicy; } void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res, - boolean enqueueOomAdj) { + boolean enqueueOomAdj, Intent intent) { boolean inDestroying = mDestroyingServices.contains(r); if (r != null) { + boolean skipOomAdj = false; if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) { // This is a call from a service start... take care of // book-keeping. @@ -6177,14 +6362,19 @@ public final class ActiveServices { // Fake it to keep from ANR due to orphaned entry. r.executeNesting = 1; } + } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_REBIND + || type == ActivityThread.SERVICE_DONE_EXECUTING_UNBIND) { + final Intent.FilterComparison filter = new Intent.FilterComparison(intent); + final IntentBindRecord b = r.bindings.get(filter); + skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b != null && b.mSkippedOomAdj; } - final long origId = Binder.clearCallingIdentity(); + final long origId = mAm.mInjector.clearCallingIdentity(); serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj, - OOM_ADJ_REASON_EXECUTING_SERVICE); - Binder.restoreCallingIdentity(origId); + skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_EXECUTING_SERVICE); + mAm.mInjector.restoreCallingIdentity(origId); } else { Slog.w(TAG, "Done executing unknown service from pid " - + Binder.getCallingPid()); + + mAm.mInjector.getCallingPid()); } } @@ -6236,10 +6426,17 @@ public final class ActiveServices { mDestroyingServices.remove(r); r.bindings.clear(); } - if (enqueueOomAdj) { - mAm.enqueueOomAdjTargetLocked(r.app); + boolean oomAdjusted = false; + if (oomAdjReason != OOM_ADJ_REASON_NONE) { + if (enqueueOomAdj) { + mAm.enqueueOomAdjTargetLocked(r.app); + } else { + mAm.updateOomAdjLocked(r.app, oomAdjReason); + } + oomAdjusted = true; } else { - mAm.updateOomAdjLocked(r.app, oomAdjReason); + // Skip oom adj if it wasn't bumped during the bumpServiceExecutingLocked() + oomAdjusted = false; } } r.executeFg = false; @@ -6296,7 +6493,7 @@ public final class ActiveServices { "realStartServiceLocked: " + sr.shortInstanceName); } realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg, - true); + true, SERVICE_BIND_OOMADJ_POLICY_LEGACY); } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -6786,6 +6983,7 @@ public final class ActiveServices { } psr.stopAllExecutingServices(); + psr.noteScheduleServiceTimeoutPending(false); } ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) { @@ -6836,7 +7034,7 @@ public final class ActiveServices { ArrayList<ActivityManager.RunningServiceInfo> res = new ArrayList<ActivityManager.RunningServiceInfo>(); - final long ident = Binder.clearCallingIdentity(); + final long ident = mAm.mInjector.clearCallingIdentity(); try { if (canInteractAcrossUsers) { int[] users = mAm.mUserController.getUsers(); @@ -6878,14 +7076,14 @@ public final class ActiveServices { } } } finally { - Binder.restoreCallingIdentity(ident); + mAm.mInjector.restoreCallingIdentity(ident); } return res; } public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) { - int userId = UserHandle.getUserId(Binder.getCallingUid()); + int userId = UserHandle.getUserId(mAm.mInjector.getCallingUid()); ServiceRecord r = getServiceByNameLocked(name, userId); if (r != null) { ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); @@ -7077,6 +7275,7 @@ public final class ActiveServices { final long delay = proc.mServices.shouldExecServicesFg() ? mAm.mConstants.SERVICE_TIMEOUT : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT; mActiveServiceAnrTimer.start(proc, delay); + proc.mServices.noteScheduleServiceTimeoutPending(false); } void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ca04e41769ee..374a17a3a23f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -135,6 +135,7 @@ import static android.view.Display.INVALID_DISPLAY; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH; import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED; +import static com.android.sdksandbox.flags.Flags.sdkSandboxInstrumentationInfo; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK; @@ -1751,7 +1752,7 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("mProcLock") private long mLastBinderHeavyHitterAutoSamplerStart = 0L; - final AppProfiler mAppProfiler; + AppProfiler mAppProfiler; private static final int INDEX_NATIVE_PSS = 0; private static final int INDEX_NATIVE_SWAP_PSS = 1; @@ -2496,7 +2497,7 @@ public class ActivityManagerService extends IActivityManager.Stub mInjector = injector; mContext = mInjector.getContext(); mUiContext = null; - mAppErrors = null; + mAppErrors = injector.getAppErrors(); mPackageWatchdog = null; mAppOpsService = mInjector.getAppOpsService(null /* recentAccessesFile */, null /* storageFile */, null /* handler */); @@ -2514,7 +2515,7 @@ public class ActivityManagerService extends IActivityManager.Stub ? new OomAdjusterModernImpl(this, mProcessList, activeUids, handlerThread) : new OomAdjuster(this, mProcessList, activeUids, handlerThread); - mIntentFirewall = null; + mIntentFirewall = injector.getIntentFirewall(); mProcessStats = new ProcessStatsService(this, mContext.getCacheDir()); mCpHelper = new ContentProviderHelper(this, false); mServices = mInjector.getActiveServices(this); @@ -13889,13 +13890,15 @@ public class ActivityManagerService extends IActivityManager.Stub } } - public void serviceDoneExecuting(IBinder token, int type, int startId, int res) { + @Override + public void serviceDoneExecuting(IBinder token, int type, int startId, int res, Intent intent) { synchronized(this) { if (!(token instanceof ServiceRecord)) { Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token); throw new IllegalArgumentException("Invalid service token"); } - mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false); + mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false, + intent); } } @@ -16139,10 +16142,22 @@ public class ActivityManagerService extends IActivityManager.Stub } final ApplicationInfo sdkSandboxInfo; + final String processName; try { - sdkSandboxInfo = - sandboxManagerLocal.getSdkSandboxApplicationInfoForInstrumentation( - sdkSandboxClientAppInfo, isSdkInSandbox); + if (sdkSandboxInstrumentationInfo()) { + sdkSandboxInfo = + sandboxManagerLocal.getSdkSandboxApplicationInfoForInstrumentation( + sdkSandboxClientAppInfo, isSdkInSandbox); + processName = sdkSandboxInfo.processName; + } else { + final PackageManager pm = mContext.getPackageManager(); + sdkSandboxInfo = + pm.getApplicationInfoAsUser(pm.getSdkSandboxPackageName(), 0, userId); + processName = + sandboxManagerLocal.getSdkSandboxProcessNameForInstrumentation( + sdkSandboxClientAppInfo); + sdkSandboxInfo.uid = Process.toSdkSandboxUid(sdkSandboxClientAppInfo.uid); + } } catch (NameNotFoundException e) { reportStartInstrumentationFailureLocked( watcher, className, "Can't find SdkSandbox package"); @@ -16151,7 +16166,7 @@ public class ActivityManagerService extends IActivityManager.Stub ActiveInstrumentation activeInstr = new ActiveInstrumentation(this); activeInstr.mClass = className; - activeInstr.mTargetProcesses = new String[]{sdkSandboxInfo.processName}; + activeInstr.mTargetProcesses = new String[]{processName}; activeInstr.mTargetInfo = sdkSandboxInfo; activeInstr.mIsSdkInSandbox = isSdkInSandbox; activeInstr.mProfileFile = profileFile; @@ -16194,7 +16209,7 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessRecord app = addAppLocked( sdkSandboxInfo, - sdkSandboxInfo.processName, + processName, /* isolated= */ false, /* isSdkSandbox= */ true, sdkSandboxInfo.uid, @@ -20236,6 +20251,36 @@ public class ActivityManagerService extends IActivityManager.Stub } return broadcastQueues; } + + /** @see Binder#getCallingUid */ + public int getCallingUid() { + return Binder.getCallingUid(); + } + + /** @see Binder#getCallingPid */ + public int getCallingPid() { + return Binder.getCallingUid(); + } + + /** @see Binder#clearCallingIdentity */ + public long clearCallingIdentity() { + return Binder.clearCallingIdentity(); + } + + /** @see Binder#clearCallingIdentity */ + public void restoreCallingIdentity(long ident) { + Binder.restoreCallingIdentity(ident); + } + + /** @return the default instance of AppErrors */ + public AppErrors getAppErrors() { + return null; + } + + /** @return the default instance of intent firewall */ + public IntentFirewall getIntentFirewall() { + return null; + } } @Override diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 3487ae3c6e6c..4f46ecdb9228 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -422,7 +422,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.setExternalStatsSyncLocked(mWorker); mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); - mStats.startTrackingSystemServerCpuTime(); + if (!Flags.disableSystemServicePowerAttr()) { + mStats.startTrackingSystemServerCpuTime(); + } mAggregatedPowerStatsConfig = createAggregatedPowerStatsConfig(); mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig); diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 626b70b51093..d92a24b82764 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -1419,6 +1419,11 @@ public final class CachedAppOptimizer { } @GuardedBy({"mAm", "mProcLock"}) + void freezeAppAsyncAtEarliestLSP(ProcessRecord app) { + freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, 0)); + } + + @GuardedBy({"mAm", "mProcLock"}) void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis, boolean force) { final ProcessCachedOptimizerRecord opt = app.mOptRecord; @@ -1714,6 +1719,14 @@ public final class CachedAppOptimizer { compactApp(frozenProc, CompactProfile.FULL, CompactSource.APP, false); } } + frozenProc.onProcessFrozen(); + } + + /** + * Callback received when an attempt to freeze a process is cancelled (failed). + */ + void onProcessFrozenCancelled(ProcessRecord app) { + app.onProcessFrozenCancelled(); } /** @@ -2203,6 +2216,8 @@ public final class CachedAppOptimizer { onProcessFrozen(proc); removeMessages(DEADLOCK_WATCHDOG_MSG); sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS); + } else { + onProcessFrozenCancelled(proc); } } break; case REPORT_UNFREEZE_MSG: { @@ -2460,7 +2475,7 @@ public final class CachedAppOptimizer { pr = mAm.mPidsSelfLocked.get(blocked); } if (pr != null - && pr.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ) { + && pr.mState.getCurAdj() < ProcessList.FREEZER_CUTOFF_ADJ) { Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks " + pr.processName + " (" + blocked + ")"); // Found at least one blocked non-cached process diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 30f21a65b5b1..cb7898d8b862 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -35,6 +35,7 @@ import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_E import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; import static com.android.server.am.ActivityManagerService.TAG_MU; +import static com.android.server.am.Flags.serviceBindingOomAdjPolicy; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -319,8 +320,10 @@ public class ContentProviderHelper { checkTime(startTime, "getContentProviderImpl: before updateOomAdj"); final int verifiedAdj = cpr.proc.mState.getVerifiedAdj(); - boolean success = mService.updateOomAdjLocked(cpr.proc, - OOM_ADJ_REASON_GET_PROVIDER); + boolean success = !serviceBindingOomAdjPolicy() + || mService.mOomAdjuster.evaluateProviderConnectionAdd(r, cpr.proc) + ? mService.updateOomAdjLocked(cpr.proc, OOM_ADJ_REASON_GET_PROVIDER) + : true; // XXX things have changed so updateOomAdjLocked doesn't actually tell us // if the process has been successfully adjusted. So to reduce races with // it, we will check whether the process still exists. Note that this doesn't @@ -1529,7 +1532,9 @@ public class ContentProviderHelper { } mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName); - if (updateOomAdj) { + if (updateOomAdj && (!serviceBindingOomAdjPolicy() + || mService.mOomAdjuster.evaluateProviderConnectionRemoval(conn.client, + cpr.proc))) { mService.updateOomAdjLocked(conn.provider.proc, OOM_ADJ_REASON_REMOVE_PROVIDER); } } diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java index abc7ab110f89..db47e3f26a6d 100644 --- a/services/core/java/com/android/server/am/IntentBindRecord.java +++ b/services/core/java/com/android/server/am/IntentBindRecord.java @@ -46,9 +46,17 @@ final class IntentBindRecord { boolean hasBound; /** Set when the service's onUnbind() has asked to be told about new clients. */ boolean doRebind; - + String stringName; // caching of toString - + + /** + * Mark if we've skipped oom adj update before calling into the {@link Service#onBind()} + * or {@link Service#onUnbind()}. + * + * <p>If it's true, we'll skip the oom adj update too during the serviceDoneExecuting. + */ + boolean mSkippedOomAdj; + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("service="); pw.println(service); dumpInService(pw, prefix); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index ef7a0e058db0..862542e3d3d0 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -103,6 +103,7 @@ import static com.android.server.am.ProcessList.CACHED_APP_IMPORTANCE_LEVELS; import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ; import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ; +import static com.android.server.am.ProcessList.FREEZER_CUTOFF_ADJ; import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ; import static com.android.server.am.ProcessList.HOME_APP_ADJ; import static com.android.server.am.ProcessList.INVALID_ADJ; @@ -2309,7 +2310,7 @@ public class OomAdjuster { } computeServiceHostOomAdjLSP(cr, app, cr.binding.client, now, topApp, doingAll, - cycleReEval, computeClients, oomAdjReason, cachedAdj, true); + cycleReEval, computeClients, oomAdjReason, cachedAdj, true, false); adj = state.getCurRawAdj(); procState = state.getCurRawProcState(); @@ -2341,7 +2342,7 @@ public class OomAdjuster { ContentProviderConnection conn = cpr.connections.get(i); ProcessRecord client = conn.client; computeProviderHostOomAdjLSP(conn, app, client, now, topApp, doingAll, - cycleReEval, computeClients, oomAdjReason, cachedAdj, true); + cycleReEval, computeClients, oomAdjReason, cachedAdj, true, false); adj = state.getCurRawAdj(); procState = state.getCurRawProcState(); @@ -2558,17 +2559,18 @@ public class OomAdjuster { } @GuardedBy({"mService", "mProcLock"}) - protected void computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app, + protected boolean computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app, ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll, boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj, - boolean couldRecurse) { + boolean couldRecurse, boolean dryRun) { if (app.isPendingFinishAttach()) { // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here. - return; + return false; } final ProcessStateRecord state = app.mState; ProcessStateRecord cstate = client.mState; + boolean updated = false; if (couldRecurse) { if (app.isSdkSandbox && cr.binding.attributedClient != null) { @@ -2599,19 +2601,25 @@ public class OomAdjuster { final int prevRawAdj = adj; final int prevProcState = procState; final int prevSchedGroup = schedGroup; + final int prevCapability = capability; final int appUid = app.info.uid; final int logUid = mService.mCurOomAdjUid; - state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp() - || cstate.isCurBoundByNonBgRestrictedApp() - || clientProcState <= PROCESS_STATE_BOUND_TOP - || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE - && !cstate.isBackgroundRestricted())); + if (!dryRun) { + state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp() + || cstate.isCurBoundByNonBgRestrictedApp() + || clientProcState <= PROCESS_STATE_BOUND_TOP + || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE + && !cstate.isBackgroundRestricted())); + } if (client.mOptRecord.shouldNotFreeze()) { // Propagate the shouldNotFreeze flag down the bindings. - app.mOptRecord.setShouldNotFreeze(true); + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } boolean trackedProcState = false; @@ -2653,7 +2661,7 @@ public class OomAdjuster { } if (couldRecurse && shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) { - return; + return false; } if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { @@ -2666,7 +2674,10 @@ public class OomAdjuster { if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) { // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen. if (clientAdj < CACHED_APP_MIN_ADJ) { - app.mOptRecord.setShouldNotFreeze(true); + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } // Not doing bind OOM management, so treat // this guy more like a started service. @@ -2678,7 +2689,10 @@ public class OomAdjuster { if (adj > clientAdj) { adjType = "cch-bound-ui-services"; } - state.setCached(false); + if (state.setCached(false, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } clientAdj = adj; clientProcState = procState; } else { @@ -2721,7 +2735,9 @@ public class OomAdjuster { newAdj = PERSISTENT_SERVICE_ADJ; schedGroup = SCHED_GROUP_DEFAULT; procState = ActivityManager.PROCESS_STATE_PERSISTENT; - cr.trackProcState(procState, mAdjSeq); + if (!dryRun) { + cr.trackProcState(procState, mAdjSeq); + } trackedProcState = true; } } else if (cr.hasFlag(Context.BIND_NOT_PERCEPTIBLE) @@ -2762,11 +2778,16 @@ public class OomAdjuster { } } if (!cstate.isCached()) { - state.setCached(false); + if (state.setCached(false, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } if (adj > newAdj) { adj = newAdj; - state.setCurRawAdj(adj); + if (state.setCurRawAdj(adj, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + } adjType = "service"; } } @@ -2833,25 +2854,35 @@ public class OomAdjuster { if (cr.hasFlag(Context.BIND_SCHEDULE_LIKE_TOP_APP) && clientIsSystem) { schedGroup = SCHED_GROUP_TOP_APP; - state.setScheduleLikeTopApp(true); + if (dryRun) { + if (prevSchedGroup < schedGroup) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } else { + state.setScheduleLikeTopApp(true); + } } - if (!trackedProcState) { + if (!trackedProcState && !dryRun) { cr.trackProcState(clientProcState, mAdjSeq); } if (procState > clientProcState) { procState = clientProcState; - state.setCurRawProcState(procState); + if (state.setCurRawProcState(procState, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } if (adjType == null) { adjType = "service"; } } if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND - && cr.hasFlag(Context.BIND_SHOWING_UI)) { + && cr.hasFlag(Context.BIND_SHOWING_UI) && !dryRun) { app.setPendingUiClean(true); } - if (adjType != null) { + if (adjType != null && !dryRun) { state.setAdjType(adjType); state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE); @@ -2876,11 +2907,16 @@ public class OomAdjuster { // bound by an unfrozen app via a WPRI binding has to remain // unfrozen. if (clientAdj < CACHED_APP_MIN_ADJ) { - app.mOptRecord.setShouldNotFreeze(true); + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } } if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) { - app.mServices.setTreatLikeActivity(true); + if (!dryRun) { + app.mServices.setTreatLikeActivity(true); + } if (clientProcState <= PROCESS_STATE_CACHED_ACTIVITY && procState > PROCESS_STATE_CACHED_ACTIVITY) { // This is a cached process, but somebody wants us to treat it like it has @@ -2894,7 +2930,9 @@ public class OomAdjuster { if (a != null && adj > FOREGROUND_APP_ADJ && a.isActivityVisible()) { adj = FOREGROUND_APP_ADJ; - state.setCurRawAdj(adj); + if (state.setCurRawAdj(adj, dryRun)) { + return true; + } if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND)) { if (cr.hasFlag(Context.BIND_IMPORTANT)) { schedGroup = SCHED_GROUP_TOP_APP_BOUND; @@ -2902,16 +2940,18 @@ public class OomAdjuster { schedGroup = SCHED_GROUP_DEFAULT; } } - state.setCached(false); - state.setAdjType("service"); - state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo - .REASON_SERVICE_IN_USE); - state.setAdjSource(a); - state.setAdjSourceProcState(procState); - state.setAdjTarget(cr.binding.service.instanceName); - if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { - reportOomAdjMessageLocked(TAG_OOM_ADJ, - "Raise to service w/activity: " + app); + if (!dryRun) { + state.setCached(false); + state.setAdjType("service"); + state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo + .REASON_SERVICE_IN_USE); + state.setAdjSource(a); + state.setAdjSourceProcState(procState); + state.setAdjTarget(cr.binding.service.instanceName); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise to service w/activity: " + app); + } } } } @@ -2922,7 +2962,15 @@ public class OomAdjuster { if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { capability &= ~PROCESS_CAPABILITY_BFSL; } + if (!updated) { + updated = adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup + || (capability != prevCapability + && (capability & prevCapability) == prevCapability); + } + if (dryRun) { + return updated; + } if (adj < prevRawAdj) { schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup); } @@ -2935,15 +2983,16 @@ public class OomAdjuster { state.setCurCapability(capability); state.setEmpty(false); + return updated; } - protected void computeProviderHostOomAdjLSP(ContentProviderConnection conn, ProcessRecord app, - ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll, - boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj, - boolean couldRecurse) { + protected boolean computeProviderHostOomAdjLSP(ContentProviderConnection conn, + ProcessRecord app, ProcessRecord client, long now, ProcessRecord topApp, + boolean doingAll, boolean cycleReEval, boolean computeClients, int oomAdjReason, + int cachedAdj, boolean couldRecurse, boolean dryRun) { if (app.isPendingFinishAttach()) { // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here. - return; + return false; } final ProcessStateRecord state = app.mState; @@ -2951,7 +3000,7 @@ public class OomAdjuster { if (client == app) { // Being our own client is not interesting. - return; + return false; } if (couldRecurse) { if (computeClients) { @@ -2964,7 +3013,7 @@ public class OomAdjuster { if (shouldSkipDueToCycle(app, cstate, state.getCurRawProcState(), state.getCurRawAdj(), cycleReEval)) { - return; + return false; } } @@ -2979,6 +3028,7 @@ public class OomAdjuster { final int prevRawAdj = adj; final int prevProcState = procState; final int prevSchedGroup = schedGroup; + final int prevCapability = capability; final int appUid = app.info.uid; final int logUid = mService.mCurOomAdjUid; @@ -2995,14 +3045,19 @@ public class OomAdjuster { } if (client.mOptRecord.shouldNotFreeze()) { // Propagate the shouldNotFreeze flag down the bindings. - app.mOptRecord.setShouldNotFreeze(true); + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } - state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp() - || cstate.isCurBoundByNonBgRestrictedApp() - || clientProcState <= PROCESS_STATE_BOUND_TOP - || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE - && !cstate.isBackgroundRestricted())); + if (!dryRun) { + state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp() + || cstate.isCurBoundByNonBgRestrictedApp() + || clientProcState <= PROCESS_STATE_BOUND_TOP + || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE + && !cstate.isBackgroundRestricted())); + } String adjType = null; if (adj > clientAdj) { @@ -3011,10 +3066,16 @@ public class OomAdjuster { adjType = "cch-ui-provider"; } else { adj = Math.max(clientAdj, FOREGROUND_APP_ADJ); - state.setCurRawAdj(adj); + if (state.setCurRawAdj(adj, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } adjType = "provider"; } - state.setCached(state.isCached() & cstate.isCached()); + if (state.setCached(state.isCached() & cstate.isCached(), dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) { @@ -3028,15 +3089,20 @@ public class OomAdjuster { } } - conn.trackProcState(clientProcState, mAdjSeq); + if (!dryRun) { + conn.trackProcState(clientProcState, mAdjSeq); + } if (procState > clientProcState) { procState = clientProcState; - state.setCurRawProcState(procState); + if (state.setCurRawProcState(procState, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } if (cstate.getCurrentSchedulingGroup() > schedGroup) { schedGroup = SCHED_GROUP_DEFAULT; } - if (adjType != null) { + if (adjType != null && !dryRun) { state.setAdjType(adjType); state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE); @@ -3056,6 +3122,12 @@ public class OomAdjuster { capability &= ~PROCESS_CAPABILITY_BFSL; } + if (dryRun && (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup + || (capability != prevCapability + && (capability & prevCapability) == prevCapability))) { + return true; + } + if (adj < prevRawAdj) { schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup); } @@ -3068,6 +3140,7 @@ public class OomAdjuster { state.setCurCapability(capability); state.setEmpty(false); + return false; } protected int getDefaultCapability(ProcessRecord app, int procState) { @@ -3342,7 +3415,7 @@ public class OomAdjuster { changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES; } - updateAppFreezeStateLSP(app, oomAdjReson); + updateAppFreezeStateLSP(app, oomAdjReson, false); if (state.getReportedProcState() != state.getCurProcState()) { state.setReportedProcState(state.getCurProcState()); @@ -3727,7 +3800,8 @@ public class OomAdjuster { } @GuardedBy({"mService", "mProcLock"}) - private void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) { + void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason, + boolean immediate) { if (!mCachedAppOptimizer.useFreezer()) { return; } @@ -3746,10 +3820,14 @@ public class OomAdjuster { final ProcessStateRecord state = app.mState; // Use current adjustment when freezing, set adjustment when unfreezing. - if (state.getCurAdj() >= CACHED_APP_MIN_ADJ && !opt.isFrozen() + if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen() && !opt.shouldNotFreeze()) { - mCachedAppOptimizer.freezeAppAsyncLSP(app); - } else if (state.getSetAdj() < CACHED_APP_MIN_ADJ) { + if (!immediate) { + mCachedAppOptimizer.freezeAppAsyncLSP(app); + } else { + mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app); + } + } else if (state.getSetAdj() < FREEZER_CUTOFF_ADJ) { mCachedAppOptimizer.unfreezeAppLSP(app, CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); } @@ -3826,4 +3904,89 @@ public class OomAdjuster { // The caller will set the initial value in this implementation. return app.mState.isCurBoundByNonBgRestrictedApp(); } + + /** + * Evaluate the service connection, return {@code true} if the client will change the state + * of the service host process by the given connection. + */ + @GuardedBy("mService") + boolean evaluateServiceConnectionAdd(ProcessRecord client, ProcessRecord app, + ConnectionRecord cr) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + if (app.getSetAdj() <= client.getSetAdj() + && app.getSetProcState() <= client.getSetProcState() + && ((app.getSetCapability() & client.getSetCapability()) + == client.getSetCapability() + || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES + | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS))) { + // The service host process has better states than the client, so no change. + return false; + } + // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive + // since it's only evaluating one service connection. + return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(), + mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, + CACHED_APP_MIN_ADJ, false, true /* dryRun */); + } + + @GuardedBy("mService") + boolean evaluateServiceConnectionRemoval(ProcessRecord client, ProcessRecord app, + ConnectionRecord cr) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + + if (app.getSetAdj() < client.getSetAdj() + && app.getSetProcState() < client.getSetProcState()) { + // The service host process has better states than the client. + if (((app.getSetCapability() & client.getSetCapability()) == PROCESS_CAPABILITY_NONE) + || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES + | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) { + // The service host app doesn't get any capabilities from the client. + return false; + } + } + return true; + } + + @GuardedBy("mService") + boolean evaluateProviderConnectionAdd(ProcessRecord client, ProcessRecord app) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + if (app.getSetAdj() <= client.getSetAdj() + && app.getSetProcState() <= client.getSetProcState()) { + // The provider host process has better states than the client, so no change. + return false; + } + return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(), + mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, + false, true /* dryRun */); + } + + @GuardedBy("mService") + boolean evaluateProviderConnectionRemoval(ProcessRecord client, ProcessRecord app) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + if (app.getSetAdj() < client.getSetAdj() + && app.getSetProcState() < client.getSetProcState()) { + // The provider host process has better states than the client, so no change. + return false; + } + return true; + } + + private boolean evaluateConnectionPrelude(ProcessRecord client, ProcessRecord app) { + if (client == null || app == null) { + return true; + } + if (app.isSdkSandbox || app.isolated || app.isKilledByAm() || app.isKilled()) { + // Let's always re-evaluate them for now. + return true; + } + return false; + } } diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index 5a3fbe9a66ac..f85b03e8b4eb 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -1002,7 +1002,7 @@ public class OomAdjusterModernImpl extends OomAdjuster { computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false, - oomAdjReason, cachedAdj, false); + oomAdjReason, cachedAdj, false, false); } for (int i = psr.numberOfSdkSandboxConnections() - 1; i >= 0; i--) { @@ -1018,7 +1018,7 @@ public class OomAdjusterModernImpl extends OomAdjuster { } computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false, - oomAdjReason, cachedAdj, false); + oomAdjReason, cachedAdj, false, false); } final ProcessProviderRecord ppr = app.mProviders; @@ -1035,7 +1035,7 @@ public class OomAdjusterModernImpl extends OomAdjuster { } computeProviderHostOomAdjLSP(cpc, provider, app, now, topApp, fullUpdate, false, false, - oomAdjReason, cachedAdj, false); + oomAdjReason, cachedAdj, false, false); } } } diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java index a20623cd1ee9..5df910716ba6 100644 --- a/services/core/java/com/android/server/am/PendingIntentController.java +++ b/services/core/java/com/android/server/am/PendingIntentController.java @@ -30,6 +30,7 @@ import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.PendingIntent; import android.app.PendingIntentStats; +import android.app.compat.CompatChanges; import android.content.IIntentSender; import android.content.Intent; import android.os.Binder; @@ -136,6 +137,11 @@ public class PendingIntentController { + "intent creator (" + packageName + ") because this option is meant for the pending intent sender"); + if (CompatChanges.isChangeEnabled(PendingIntent.PENDING_INTENT_OPTIONS_CHECK, + callingUid)) { + throw new IllegalArgumentException("pendingIntentBackgroundActivityStartMode " + + "must not be set when creating a PendingIntent"); + } opts.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED); } diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 10d5fd3f77b6..95e130ed1194 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -406,6 +406,9 @@ public final class PendingIntentRecord extends IIntentSender.Stub { String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + if (intent != null) intent.setDefusable(true); if (options != null) options.setDefusable(true); @@ -458,6 +461,12 @@ public final class PendingIntentRecord extends IIntentSender.Stub { + key.packageName + ") because this option is meant for the pending intent " + "creator"); + if (CompatChanges.isChangeEnabled(PendingIntent.PENDING_INTENT_OPTIONS_CHECK, + callingUid)) { + throw new IllegalArgumentException( + "pendingIntentCreatorBackgroundActivityStartMode " + + "must not be set when sending a PendingIntent"); + } opts.setPendingIntentCreatorBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED); } @@ -494,9 +503,6 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } // We don't hold the controller lock beyond this point as we will be calling into AM and WM. - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - // Only system senders can declare a broadcast to be alarm-originated. We check // this here rather than in the general case handling below to fail before the other // invocation side effects such as allowlisting. diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java index f5c5ea8ae55a..a8fe734f59b7 100644 --- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java +++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java @@ -274,7 +274,20 @@ final class ProcessCachedOptimizerRecord { @GuardedBy("mProcLock") void setShouldNotFreeze(boolean shouldNotFreeze) { + setShouldNotFreeze(shouldNotFreeze, false); + } + + /** + * @return {@code true} if it's a dry run and it's going to unfreeze the process + * if it was a real run. + */ + @GuardedBy("mProcLock") + boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun) { + if (dryRun) { + return mFrozen && !shouldNotFreeze; + } mShouldNotFreeze = shouldNotFreeze; + return false; } @GuardedBy("mProcLock") diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index f5c34a5da1c1..10cd6e5bb136 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -371,6 +371,12 @@ public final class ProcessList { private static final long LMKD_RECONNECT_DELAY_MS = 1000; /** + * The cuttoff adj for the freezer, app processes with adj greater than this value will be + * eligible for the freezer. + */ + static final int FREEZER_CUTOFF_ADJ = CACHED_APP_MIN_ADJ; + + /** * Apps have no access to the private data directories of any other app, even if the other * app has made them world-readable. */ diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index e5c4a66562c3..de6f034b62ee 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -720,6 +720,11 @@ class ProcessRecord implements WindowProcessListener { return mState.getSetProcState(); } + @GuardedBy(anyOf = {"mService", "mProcLock"}) + int getSetCapability() { + return mState.getSetCapability(); + } + @GuardedBy({"mService", "mProcLock"}) public void makeActive(IApplicationThread thread, ProcessStatsService tracker) { mProfile.onProcessActive(thread, tracker); @@ -1401,8 +1406,12 @@ class ProcessRecord implements WindowProcessListener { void onProcessUnfrozen() { mProfile.onProcessUnfrozen(); + mServices.onProcessUnfrozen(); } + void onProcessFrozenCancelled() { + mServices.onProcessFrozenCancelled(); + } /* * Delete all packages from list except the package indicated in info @@ -1644,6 +1653,13 @@ class ProcessRecord implements WindowProcessListener { return mWasForceStopped; } + boolean isFreezable() { + return mService.mOomAdjuster.mCachedAppOptimizer.useFreezer() + && !mOptRecord.isFreezeExempt() + && !mOptRecord.shouldNotFreeze() + && mState.getCurAdj() >= ProcessList.FREEZER_CUTOFF_ADJ; + } + /** * Traverses all client processes and feed them to consumer. */ diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java index f5f2b102596b..57d233e7c503 100644 --- a/services/core/java/com/android/server/am/ProcessServiceRecord.java +++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java @@ -19,6 +19,8 @@ package com.android.server.am; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_FOREGROUND_SERVICE; +import static com.android.server.am.Flags.serviceBindingOomAdjPolicy; + import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; @@ -144,6 +146,11 @@ final class ProcessServiceRecord { */ private ArraySet<Integer> mBoundClientUids = new ArraySet<>(); + /** + * The process should schedule a service timeout timer but haven't done so. + */ + private boolean mScheduleServiceTimeoutPending; + final ProcessRecord mApp; private final ActivityManagerService mService; @@ -657,6 +664,41 @@ final class ProcessServiceRecord { setHasClientActivities(false); } + @GuardedBy("mService") + void noteScheduleServiceTimeoutPending(boolean pending) { + mScheduleServiceTimeoutPending = pending; + } + + @GuardedBy("mService") + boolean isScheduleServiceTimeoutPending() { + return mScheduleServiceTimeoutPending; + } + + @GuardedBy("mService") + void onProcessUnfrozen() { + scheduleServiceTimeoutIfNeededLocked(); + } + + @GuardedBy("mService") + void onProcessFrozenCancelled() { + scheduleServiceTimeoutIfNeededLocked(); + } + + @GuardedBy("mService") + private void scheduleServiceTimeoutIfNeededLocked() { + if (!serviceBindingOomAdjPolicy()) { + return; + } + if (mScheduleServiceTimeoutPending && mExecutingServices.size() > 0) { + mService.mServices.scheduleServiceTimeoutLocked(mApp); + // We'll need to reset the executingStart since the app was frozen. + final long now = SystemClock.uptimeMillis(); + for (int i = 0, size = mExecutingServices.size(); i < size; i++) { + mExecutingServices.valueAt(i).executingStart = now; + } + } + } + void dump(PrintWriter pw, String prefix, long nowUptime) { if (mHasForegroundServices || mApp.mState.getForcingToImportant() != null) { pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices); @@ -701,5 +743,10 @@ final class ProcessServiceRecord { pw.print(prefix); pw.print(" - "); pw.println(mConnections.valueAt(i)); } } + if (serviceBindingOomAdjPolicy()) { + pw.print(prefix); + pw.print("scheduleServiceTimeoutPending="); + pw.println(mScheduleServiceTimeoutPending); + } } } diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 3391ec7c4e68..8362eaf76d55 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -479,9 +479,22 @@ final class ProcessStateRecord { @GuardedBy({"mService", "mProcLock"}) void setCurRawAdj(int curRawAdj) { + setCurRawAdj(curRawAdj, false); + } + + /** + * @return {@code true} if it's a dry run and it's going to bump the adj score of the process + * if it was a real run. + */ + @GuardedBy({"mService", "mProcLock"}) + boolean setCurRawAdj(int curRawAdj, boolean dryRun) { + if (dryRun) { + return mCurRawAdj > curRawAdj; + } mCurRawAdj = curRawAdj; mApp.getWindowProcessController().setPerceptible( curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ); + return false; } @GuardedBy(anyOf = {"mService", "mProcLock"}) @@ -594,7 +607,20 @@ final class ProcessStateRecord { @GuardedBy({"mService", "mProcLock"}) void setCurRawProcState(int curRawProcState) { + setCurRawProcState(curRawProcState, false); + } + + /** + * @return {@code true} if it's a dry run and it's going to bump the procstate of the process + * if it was a real run. + */ + @GuardedBy({"mService", "mProcLock"}) + boolean setCurRawProcState(int curRawProcState, boolean dryRun) { + if (dryRun) { + return mCurRawProcState > curRawProcState; + } mCurRawProcState = curRawProcState; + return false; } @GuardedBy(anyOf = {"mService", "mProcLock"}) @@ -900,7 +926,20 @@ final class ProcessStateRecord { @GuardedBy("mService") void setCached(boolean cached) { + setCached(cached, false); + } + + /** + * @return {@code true} if it's a dry run and it's going to uncache the process + * if it was a real run. + */ + @GuardedBy("mService") + boolean setCached(boolean cached, boolean dryRun) { + if (dryRun) { + return mCached && !cached; + } mCached = cached; + return false; } @GuardedBy("mService") diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index 654aebd89de2..31d9cc927223 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -35,3 +35,10 @@ flag { description: "Enable the new FGS restriction logic" bug: "276963716" } + +flag { + name: "service_binding_oom_adj_policy" + namespace: "backstage_power" + description: "Optimize the service bindings by different policies like skipping oom adjuster" + bug: "318717054" +} diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index df179a9b6d65..5b23364cf546 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -138,6 +138,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Random; import java.util.Set; @@ -3870,8 +3871,12 @@ public class SyncManager { final SyncStorageEngine.EndPoint info = syncOperation.target; if (activeSyncContext.mIsLinkedToDeath) { - activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); - activeSyncContext.mIsLinkedToDeath = false; + try { + activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); + activeSyncContext.mIsLinkedToDeath = false; + } catch (NoSuchElementException e) { + Slog.wtf(TAG, "Failed to unlink active sync adapter to death", e); + } } final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; String historyMessage; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 9cf9119aff82..93addcd84d12 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1656,14 +1656,16 @@ public final class DisplayManagerService extends SystemService { ContentRecordingSession session = null; try { if (projection != null) { - IBinder launchCookie = projection.getLaunchCookie(); - if (launchCookie == null) { + IBinder taskWindowContainerToken = projection.getLaunchCookie() == null ? null + : projection.getLaunchCookie().binder; + if (taskWindowContainerToken == null) { // Record a particular display. session = ContentRecordingSession.createDisplaySession( virtualDisplayConfig.getDisplayIdToMirror()); } else { // Record a single task indicated by the launch cookie. - session = ContentRecordingSession.createTaskSession(launchCookie); + session = ContentRecordingSession.createTaskSession( + taskWindowContainerToken); } } } catch (RemoteException e) { @@ -2776,17 +2778,17 @@ public final class DisplayManagerService extends SystemService { } private ScreenCapture.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) { + final ScreenCapture.DisplayCaptureArgs captureArgs; synchronized (mSyncRoot) { final IBinder token = getDisplayToken(displayId); if (token == null) { return null; } - final ScreenCapture.DisplayCaptureArgs captureArgs = - new ScreenCapture.DisplayCaptureArgs.Builder(token) + captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token) .build(); - return ScreenCapture.captureDisplay(captureArgs); } + return ScreenCapture.captureDisplay(captureArgs); } @VisibleForTesting diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 82461fa647b3..8b4e1ff4981f 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -394,6 +394,8 @@ public class DisplayModeDirector { if (mDisplayObserver.isExternalDisplayLocked(displayId)) { primarySummary.maxRenderFrameRate = Math.max(baseMode.getRefreshRate(), primarySummary.maxRenderFrameRate); + appRequestSummary.maxRenderFrameRate = Math.max(baseMode.getRefreshRate(), + appRequestSummary.maxRenderFrameRate); } return new DesiredDisplayModeSpecs(baseMode.getModeId(), diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index ba4d320df38c..731c78e20c7a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -514,6 +514,18 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { protected int handleRoutingInformation(HdmiCecMessage message) { assertRunOnServiceThread(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); + HdmiDeviceInfo sourceDevice = mService.getHdmiCecNetwork() + .getCecDeviceInfo(message.getSource()); + // Ignore <Routing Information> messages pointing to the same physical address as the + // message sender. In this case, we shouldn't consider the sender to be the active source. + // See more b/321771821#comment7. + if (sourceDevice != null + && sourceDevice.getLogicalAddress() != Constants.ADDR_TV + && sourceDevice.getPhysicalAddress() == physicalAddress) { + Slog.d(TAG, "<Routing Information> is ignored, it is pointing to the same physical" + + " address as the message sender"); + return Constants.HANDLED; + } handleRoutingChangeAndInformation(physicalAddress, message); return Constants.HANDLED; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 34e75c087864..46061a56631c 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -482,6 +482,22 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @Override @ServiceThreadOnly @Constants.HandleMessageResult + protected int handleStandby(HdmiCecMessage message) { + assertRunOnServiceThread(); + + // Ignore <Standby> from non-active source device. + if (getActiveSource().logicalAddress != message.getSource()) { + Slog.d(TAG, "<Standby> was not sent by the current active source, ignoring." + + " Current active source has logical address " + + getActiveSource().logicalAddress); + return Constants.HANDLED; + } + return super.handleStandby(message); + } + + @Override + @ServiceThreadOnly + @Constants.HandleMessageResult protected int handleInactiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); // Seq #10 diff --git a/services/core/java/com/android/server/inputmethod/ClientController.java b/services/core/java/com/android/server/inputmethod/ClientController.java index ece236a5f18c..86f4db959409 100644 --- a/services/core/java/com/android/server/inputmethod/ClientController.java +++ b/services/core/java/com/android/server/inputmethod/ClientController.java @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.PackageManagerInternal; import android.os.IBinder; import android.os.RemoteException; @@ -29,6 +30,7 @@ import com.android.internal.inputmethod.IRemoteInputConnection; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** * Store and manage {@link InputMethodManagerService} clients. This class was designed to be a @@ -62,7 +64,7 @@ final class ClientController { // TODO(b/314150112): Make this field private when breaking the cycle with IMMS. @GuardedBy("ImfLock.class") - final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); + private final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); @GuardedBy("ImfLock.class") private final List<ClientControllerCallback> mCallbacks = new ArrayList<>(); @@ -145,6 +147,19 @@ final class ClientController { } @GuardedBy("ImfLock.class") + @Nullable + ClientState getClient(IBinder binder) { + return mClients.get(binder); + } + + @GuardedBy("ImfLock.class") + void forAllClients(Consumer<ClientState> consumer) { + for (int i = 0; i < mClients.size(); i++) { + consumer.accept(mClients.valueAt(i)); + } + } + + @GuardedBy("ImfLock.class") boolean verifyClientAndPackageMatch( @NonNull IInputMethodClient client, @NonNull String packageName) { final ClientState cs = mClients.get(client.asBinder()); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 4767ebd0aab0..f031b7b677ac 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -205,6 +205,7 @@ import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.IntConsumer; /** @@ -270,7 +271,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final String[] mNonPreemptibleInputMethods; - // TODO(b/314150112): Move this to ClientController. @UserIdInt private int mLastSwitchUserId; @@ -1819,10 +1819,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mLastSwitchUserId = newUserId; - if (mIsInteractive && clientToBeReset != null) { - final ClientState cs = - mClientController.mClients.get(clientToBeReset.asBinder()); + final ClientState cs = mClientController.getClient(clientToBeReset.asBinder()); if (cs == null) { // The client is already gone. return; @@ -2165,26 +2163,25 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub /** * Hide the IME if the removed user is the current user. */ + @GuardedBy("ImfLock.class") private void onClientRemoved(ClientState client) { - synchronized (ImfLock.class) { - clearClientSessionLocked(client); - clearClientSessionForAccessibilityLocked(client); - if (mCurClient == client) { - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, - null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); - if (mBoundToMethod) { - mBoundToMethod = false; - IInputMethodInvoker curMethod = getCurMethodLocked(); - if (curMethod != null) { - // When we unbind input, we are unbinding the client, so we always - // unbind ime and a11y together. - curMethod.unbindInput(); - AccessibilityManagerInternal.get().unbindInput(); - } + clearClientSessionLocked(client); + clearClientSessionForAccessibilityLocked(client); + if (mCurClient == client) { + hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, + null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); + if (mBoundToMethod) { + mBoundToMethod = false; + IInputMethodInvoker curMethod = getCurMethodLocked(); + if (curMethod != null) { + // When we unbind input, we are unbinding the client, so we always + // unbind ime and a11y together. + curMethod.unbindInput(); + AccessibilityManagerInternal.get().unbindInput(); } - mBoundToAccessibility = false; - mCurClient = null; } + mBoundToAccessibility = false; + mCurClient = null; if (mCurFocusedWindowClient == client) { mCurFocusedWindowClient = null; mCurFocusedWindowEditorInfo = null; @@ -2192,7 +2189,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } - // TODO(b/314150112): Move this to ClientController. @GuardedBy("ImfLock.class") void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) { if (mCurClient != null) { @@ -2883,11 +2879,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") void clearClientSessionsLocked() { if (getCurMethodLocked() != null) { - final int numClients = mClientController.mClients.size(); - for (int i = 0; i < numClients; ++i) { - clearClientSessionLocked(mClientController.mClients.valueAt(i)); - clearClientSessionForAccessibilityLocked(mClientController.mClients.valueAt(i)); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + clearClientSessionLocked(c); + clearClientSessionForAccessibilityLocked(c); + } + }); finishSessionLocked(mEnabledSession); for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) { @@ -3732,9 +3733,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.INVALID_USER; } - final ClientState cs = mClientController.mClients.get(client.asBinder()); + final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { - throw new IllegalArgumentException("unknown client " + client.asBinder()); + throw new IllegalArgumentException("Unknown client " + client.asBinder()); } final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus( @@ -3906,8 +3907,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // We need to check if this is the current client with // focus in the window manager, to allow this call to // be made before input is started in it. - final ClientState cs = - mClientController.mClients.get(client.asBinder()); + final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN); throw new IllegalArgumentException("unknown client " + client.asBinder()); @@ -4518,16 +4518,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void startImeTrace() { super.startImeTrace_enforcePermission(); - ImeTracing.getInstance().startTrace(null /* printwriter */); - ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { - clients = new ArrayMap<>(mClientController.mClients); - } - for (ClientState state : clients.values()) { - if (state != null) { - state.mClient.setImeTraceEnabled(true /* enabled */); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + c.mClient.setImeTraceEnabled(true /* enabled */); + } + }); } } @@ -4538,14 +4539,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub super.stopImeTrace_enforcePermission(); ImeTracing.getInstance().stopTrace(null /* printwriter */); - ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { - clients = new ArrayMap<>(mClientController.mClients); - } - for (ClientState state : clients.values()) { - if (state != null) { - state.mClient.setImeTraceEnabled(false /* enabled */); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + c.mClient.setImeTraceEnabled(false /* enabled */); + } + }); } } @@ -5779,11 +5782,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // We only have sessions when we bound to an input method. Remove this session // from all clients. if (getCurMethodLocked() != null) { - final int numClients = mClientController.mClients.size(); - for (int i = 0; i < numClients; ++i) { - clearClientSessionForAccessibilityLocked( - mClientController.mClients.valueAt(i), accessibilityConnectionId); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId); + } + }); AccessibilitySessionState session = mEnabledAccessibilitySessions.get( accessibilityConnectionId); if (session != null) { @@ -5967,19 +5974,26 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub p.println(" InputMethod #" + i + ":"); info.dump(p, " "); } + // Dump ClientController#mClients p.println(" ClientStates:"); - // TODO(b/314150112): move client related dump info to ClientController#dump - final int numClients = mClientController.mClients.size(); - for (int i = 0; i < numClients; ++i) { - final ClientState ci = mClientController.mClients.valueAt(i); - p.println(" " + ci + ":"); - p.println(" client=" + ci.mClient); - p.println(" fallbackInputConnection=" + ci.mFallbackInputConnection); - p.println(" sessionRequested=" + ci.mSessionRequested); - p.println(" sessionRequestedForAccessibility=" - + ci.mSessionRequestedForAccessibility); - p.println(" curSession=" + ci.mCurSession); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + p.println(" " + c + ":"); + p.println(" client=" + c.mClient); + p.println(" fallbackInputConnection=" + + c.mFallbackInputConnection); + p.println(" sessionRequested=" + + c.mSessionRequested); + p.println( + " sessionRequestedForAccessibility=" + + c.mSessionRequestedForAccessibility); + p.println(" curSession=" + c.mCurSession); + } + }); p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked()); @@ -6583,14 +6597,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled(); - ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { - clients = new ArrayMap<>(mClientController.mClients); - } - for (ClientState state : clients.values()) { - if (state != null) { - state.mClient.setImeTraceEnabled(isImeTraceEnabled); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + c.mClient.setImeTraceEnabled(isImeTraceEnabled); + } + }); } return ShellCommandResult.SUCCESS; } diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index cc205d4a53bd..cc58f38db65a 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -1541,8 +1541,14 @@ class SyntheticPasswordManager { */ public @NonNull AuthenticationResult unlockTokenBasedProtector( IGateKeeperService gatekeeper, long protectorId, byte[] token, int userId) { - SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME, - protectorId, userId)); + byte[] data = loadState(SP_BLOB_NAME, protectorId, userId); + if (data == null) { + AuthenticationResult result = new AuthenticationResult(); + result.gkResponse = VerifyCredentialResponse.ERROR; + Slogf.w(TAG, "spblob not found for protector %016x, user %d", protectorId, userId); + return result; + } + SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(data); return unlockTokenBasedProtectorInternal(gatekeeper, protectorId, blob.mProtectorType, token, userId); } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 1d516e2931d7..7fabdf293b39 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -34,6 +34,8 @@ import android.app.ForegroundServiceDelegationOptions; import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.usage.UsageStatsManager; +import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -68,6 +70,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; +import android.os.PersistableBundle; import android.os.PowerExemptionManager; import android.os.PowerManager; import android.os.Process; @@ -100,7 +103,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * System implementation of MediaSessionManager @@ -145,6 +150,8 @@ public class MediaSessionService extends SystemService implements Monitor { private AudioManager mAudioManager; private boolean mHasFeatureLeanback; private ActivityManagerInternal mActivityManagerInternal; + private UsageStatsManagerInternal mUsageStatsManagerInternal; + private final Set<Integer> mUserEngagingSessions = new HashSet<>(); // 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. @@ -230,6 +237,7 @@ public class MediaSessionService extends SystemService implements Monitor { mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); } @Override @@ -582,12 +590,43 @@ public class MediaSessionService extends SystemService implements Monitor { if (allowRunningInForeground) { mActivityManagerInternal.startForegroundServiceDelegate( foregroundServiceDelegationOptions, /* connection= */ null); + reportMediaInteractionEvent(record, /* userEngaged= */ true); } else { mActivityManagerInternal.stopForegroundServiceDelegate( foregroundServiceDelegationOptions); + reportMediaInteractionEvent(record, /* userEngaged= */ false); } } + private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) { + if (!android.app.usage.Flags.userInteractionTypeApi()) { + return; + } + + String packageName = record.getPackageName(); + int sessionUid = record.getUid(); + String actionToLog = null; + if (userEngaged) { + if (!mUserEngagingSessions.contains(sessionUid)) { + actionToLog = "start"; + } + mUserEngagingSessions.add(sessionUid); + } else { + if (mUserEngagingSessions.contains(sessionUid)) { + actionToLog = "stop"; + } + mUserEngagingSessions.remove(sessionUid); + } + + if (actionToLog != null) { + PersistableBundle extras = new PersistableBundle(); + extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media"); + extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, actionToLog); + mUsageStatsManagerInternal.reportUserInteractionEvent( + packageName, record.getUserId(), extras); + } + } + void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage, int callingPid, int callingUid, String callingPackage, String reason) { final long token = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 978f46808e3b..bbb19e351b5d 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -35,6 +35,7 @@ import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions.LaunchCookie; import android.app.AppOpsManager; import android.app.IProcessObserver; import android.app.compat.CompatChanges; @@ -548,8 +549,11 @@ public final class MediaProjectionManagerService extends SystemService DEFAULT_DISPLAY)); break; case RECORD_CONTENT_TASK: - setReviewedConsentSessionLocked(ContentRecordingSession.createTaskSession( - mProjectionGrant.getLaunchCookie())); + IBinder taskWindowContainerToken = + mProjectionGrant.getLaunchCookie() == null ? null + : mProjectionGrant.getLaunchCookie().binder; + setReviewedConsentSessionLocked( + ContentRecordingSession.createTaskSession(taskWindowContainerToken)); break; } } @@ -973,7 +977,7 @@ public final class MediaProjectionManagerService extends SystemService private IBinder mToken; private IBinder.DeathRecipient mDeathEater; private boolean mRestoreSystemAlertWindow; - private IBinder mLaunchCookie = null; + private LaunchCookie mLaunchCookie = null; // Values for tracking token validity. // Timeout value to compare creation time against. @@ -1186,14 +1190,14 @@ public final class MediaProjectionManagerService extends SystemService @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call - public void setLaunchCookie(IBinder launchCookie) { + public void setLaunchCookie(LaunchCookie launchCookie) { setLaunchCookie_enforcePermission(); mLaunchCookie = launchCookie; } @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call - public IBinder getLaunchCookie() { + public LaunchCookie getLaunchCookie() { getLaunchCookie_enforcePermission(); return mLaunchCookie; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 425659e38492..4da2cc9bbe20 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -10869,6 +10869,7 @@ public class NotificationManagerService extends SystemService { ArrayList<Notification.Action> smartActions = record.getSystemGeneratedSmartActions(); ArrayList<CharSequence> smartReplies = record.getSmartReplies(); if (redactSensitiveNotificationsFromUntrustedListeners() + && info != null && !mListeners.isUidTrusted(info.uid) && mListeners.hasSensitiveContent(record)) { smartActions = null; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 923be56dd545..fb95632e4893 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -840,9 +840,13 @@ public class ZenModeHelper { && !PACKAGE_ANDROID.equals(ruleToRemove.pkg)) { String deletedKey = ZenModeConfig.deletedRuleKey(ruleToRemove); if (deletedKey != null) { - ruleToRemove.deletionInstant = Instant.now(mClock); + ZenRule deletedRule = ruleToRemove.copy(); + deletedRule.deletionInstant = Instant.now(mClock); + // If the rule is restored it shouldn't be active (or snoozed). + deletedRule.snoozing = false; + deletedRule.condition = null; // Overwrites a previously-deleted rule with the same conditionId, but that's okay. - config.deletedRules.put(deletedKey, ruleToRemove); + config.deletedRules.put(deletedKey, deletedRule); } } } diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java index a5bc2c36a5a8..98b7c9610295 100644 --- a/services/core/java/com/android/server/pm/AppsFilterBase.java +++ b/services/core/java/com/android/server/pm/AppsFilterBase.java @@ -24,6 +24,7 @@ import static com.android.server.pm.AppsFilterUtils.requestsQueryAllPackages; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.Flags; import android.content.pm.SigningDetails; import android.os.Binder; import android.os.Handler; @@ -318,6 +319,11 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { existingSettings.untrackedStorage()); } + private static boolean isQueryableBySdkSandbox(int callingUid, int targetUid) { + return Flags.allowSdkSandboxQueryIntentActivities() + && targetUid == Process.getAppUidForSdkSandboxUid(callingUid); + } + /** * See * {@link AppsFilterSnapshot#shouldFilterApplication(PackageDataSnapshot, int, Object, @@ -338,9 +344,11 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { } else if (Process.isSdkSandboxUid(callingAppId)) { final int targetAppId = targetPkgSetting.getAppId(); final int targetUid = UserHandle.getUid(userId, targetAppId); - // we only allow sdk sandbox processes access to forcequeryable packages + // we only allow sdk sandbox processes access to forcequeryable packages or + // if the target app is the sandbox's client app return !isForceQueryable(targetPkgSetting.getAppId()) - && !isImplicitlyQueryable(callingUid, targetUid); + && !isImplicitlyQueryable(callingUid, targetUid) + && !isQueryableBySdkSandbox(callingUid, targetUid); } // use cache if (mCacheReady && mCacheEnabled) { diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index ada79aed9d16..e70c5ea656a0 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -1049,6 +1049,18 @@ final class InstallPackageHelper { } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } + if (Flags.improveInstallFreeze()) { + // Postpone freezer until after reconcile + for (ReconciledPackage reconciledPkg : reconciledPackages) { + InstallRequest installRequest = reconciledPkg.mInstallRequest; + String packageName = installRequest.getParsedPackage().getPackageName(); + PackageFreezer freezer = freezePackageForInstall(packageName, + UserHandle.USER_ALL, installRequest.getInstallFlags(), + "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED, + installRequest); + installRequest.setFreezer(freezer); + } + } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages"); commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds()); @@ -1613,9 +1625,12 @@ final class InstallPackageHelper { parsedPackage.setBaseApkPath(request.getApexInfo().modulePath); } - final PackageFreezer freezer = - freezePackageForInstall(pkgName, UserHandle.USER_ALL, installFlags, - "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED, request); + PackageFreezer freezer = null; + if (!Flags.improveInstallFreeze()) { + freezer = freezePackageForInstall(pkgName, UserHandle.USER_ALL, installFlags, + "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED, request); + } + boolean shouldCloseFreezerBeforeReturn = true; try { @@ -1865,9 +1880,11 @@ final class InstallPackageHelper { oldPackageState, parsedPackage, archivedPackage, replace /* clearCodeCache */, sysPkg, ps, disabledPs); } finally { - request.setFreezer(freezer); - if (shouldCloseFreezerBeforeReturn) { - freezer.close(); + if (freezer != null) { + request.setFreezer(freezer); + if (shouldCloseFreezerBeforeReturn) { + freezer.close(); + } } } } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index b5346a351f38..bc0173a90989 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -18,6 +18,10 @@ package com.android.server.pm; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME; +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_IGNORED; +import static android.app.AppOpsManager.OP_ARCHIVE_ICON_OVERLAY; +import static android.app.AppOpsManager.OP_UNARCHIVAL_CONFIRMATION; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; import static android.app.PendingIntent.FLAG_IMMUTABLE; @@ -43,6 +47,7 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.IApplicationThread; import android.app.PendingIntent; import android.app.admin.DevicePolicyCache; @@ -213,6 +218,7 @@ public class LauncherAppsService extends SystemService { private final ActivityTaskManagerInternal mActivityTaskManagerInternal; private final ShortcutServiceInternal mShortcutServiceInternal; private final PackageManagerInternal mPackageManagerInternal; + private final AppOpsManager mAppOpsManager; private final PackageCallbackList<IOnAppsChangedListener> mListeners = new PackageCallbackList<IOnAppsChangedListener>(); private final DevicePolicyManager mDpm; @@ -253,6 +259,7 @@ public class LauncherAppsService extends SystemService { LocalServices.getService(ShortcutServiceInternal.class)); mPackageManagerInternal = Objects.requireNonNull( LocalServices.getService(PackageManagerInternal.class)); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mShortcutServiceInternal.addListener(mPackageMonitor); mShortcutChangeHandler = new ShortcutChangeHandler(mUserManagerInternal); mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler); @@ -1997,6 +2004,23 @@ public class LauncherAppsService extends SystemService { } } + @Override + public void setArchiveCompatibilityOptions(boolean enableIconOverlay, + boolean enableUnarchivalConfirmation) { + int callingUid = Binder.getCallingUid(); + Binder.withCleanCallingIdentity( + () -> { + mAppOpsManager.setUidMode( + OP_ARCHIVE_ICON_OVERLAY, + callingUid, + enableIconOverlay ? MODE_ALLOWED : MODE_IGNORED); + mAppOpsManager.setUidMode( + OP_UNARCHIVAL_CONFIRMATION, + callingUid, + enableUnarchivalConfirmation ? MODE_ALLOWED : MODE_IGNORED); + }); + } + /** Checks if user is a profile of or same as listeningUser. * and the user is enabled. */ private boolean isEnabledProfileOf(UserHandle listeningUser, UserHandle user, diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index 32f56463c8de..474b5907524c 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.START_ABORTED; import static android.app.ActivityManager.START_CLASS_NOT_FOUND; import static android.app.ActivityManager.START_PERMISSION_DENIED; import static android.app.ActivityManager.START_SUCCESS; +import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap; @@ -275,11 +276,12 @@ public class PackageArchiver { Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName)); try { - // TODO(b/311709794) Make showUnarchivalConfirmation dependent on the compat options. requestUnarchive(packageName, callerPackageName, getOrCreateLauncherListener(userId, packageName), UserHandle.of(userId), - false /* showUnarchivalConfirmation= */); + getAppOpsManager().checkOp( + AppOpsManager.OP_UNARCHIVAL_CONFIRMATION, callingUid, callerPackageName) + == MODE_ALLOWED); } catch (Throwable t) { Slog.e(TAG, TextUtils.formatSimple( "Unexpected error occurred while unarchiving package %s: %s.", packageName, @@ -796,7 +798,8 @@ public class PackageArchiver { * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple * launcher activities, only one of the icons is returned arbitrarily. */ - public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) { + public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user, + String callingPackageName) { Objects.requireNonNull(packageName); Objects.requireNonNull(user); @@ -819,7 +822,13 @@ public class PackageArchiver { // TODO(b/298452477) Handle monochrome icons. // In the rare case the archived app defined more than two launcher activities, we choose // the first one arbitrarily. - return includeCloudOverlay(decodeIcon(archiveState.getActivityInfos().get(0))); + Bitmap icon = decodeIcon(archiveState.getActivityInfos().get(0)); + if (getAppOpsManager().checkOp( + AppOpsManager.OP_ARCHIVE_ICON_OVERLAY, callingUid, callingPackageName) + == MODE_ALLOWED) { + icon = includeCloudOverlay(icon); + } + return icon; } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 135bd4f911f9..dfe705a7a065 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6414,8 +6414,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override - public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user) { - return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user); + public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user, + @NonNull String callingPackageName) { + return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user, + callingPackageName); } @Override @@ -7807,7 +7809,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS - | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES + | ActivityInfo.FLAG_HARDWARE_ACCELERATED; mResolveActivity.theme = 0; mResolveActivity.exported = true; mResolveActivity.enabled = true; @@ -7841,7 +7844,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY - | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES + | ActivityInfo.FLAG_HARDWARE_ACCELERATED; mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert; mResolveActivity.exported = true; mResolveActivity.enabled = true; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7e3254de2385..7cf1d3323f05 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -517,15 +517,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile @Watched(manual = true) private final AppIdSettingMap mAppIds; - // For reading/writing settings file. - @Watched - private final WatchedArrayList<Signature> mPastSignatures; - private final SnapshotCache<WatchedArrayList<Signature>> mPastSignaturesSnapshot; - - @Watched - private final WatchedArrayMap<Long, Integer> mKeySetRefs; - private final SnapshotCache<WatchedArrayMap<Long, Integer>> mKeySetRefsSnapshot; - // Packages that have been renamed since they were first installed. // Keys are the new names of the packages, values are the original // names. The packages appear everywhere else under their original @@ -613,8 +604,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile mNextAppLinkGeneration.registerObserver(mObserver); mPendingDefaultBrowser.registerObserver(mObserver); mPendingPackages.registerObserver(mObserver); - mPastSignatures.registerObserver(mObserver); - mKeySetRefs.registerObserver(mObserver); } // CONSTRUCTOR @@ -641,12 +630,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile mCrossProfileIntentResolversSnapshot = new SnapshotCache.Auto<>( mCrossProfileIntentResolvers, mCrossProfileIntentResolvers, "Settings.mCrossProfileIntentResolvers"); - mPastSignatures = new WatchedArrayList<>(); - mPastSignaturesSnapshot = new SnapshotCache.Auto<>(mPastSignatures, mPastSignatures, - "Settings.mPastSignatures"); - mKeySetRefs = new WatchedArrayMap<>(); - mKeySetRefsSnapshot = new SnapshotCache.Auto<>(mKeySetRefs, mKeySetRefs, - "Settings.mKeySetRefs"); mPendingPackages = new WatchedArrayList<>(); mPendingPackagesSnapshot = new SnapshotCache.Auto<>(mPendingPackages, mPendingPackages, "Settings.mPendingPackages"); @@ -702,12 +685,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile mCrossProfileIntentResolversSnapshot = new SnapshotCache.Auto<>( mCrossProfileIntentResolvers, mCrossProfileIntentResolvers, "Settings.mCrossProfileIntentResolvers"); - mPastSignatures = new WatchedArrayList<>(); - mPastSignaturesSnapshot = new SnapshotCache.Auto<>(mPastSignatures, mPastSignatures, - "Settings.mPastSignatures"); - mKeySetRefs = new WatchedArrayMap<>(); - mKeySetRefsSnapshot = new SnapshotCache.Auto<>(mKeySetRefs, mKeySetRefs, - "Settings.mKeySetRefs"); mPendingPackages = new WatchedArrayList<>(); mPendingPackagesSnapshot = new SnapshotCache.Auto<>(mPendingPackages, mPendingPackages, "Settings.mPendingPackages"); @@ -799,11 +776,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile mSharedUsers.snapshot(r.mSharedUsers); mAppIds = r.mAppIds.snapshot(); - mPastSignatures = r.mPastSignaturesSnapshot.snapshot(); - mPastSignaturesSnapshot = new SnapshotCache.Sealed<>(); - mKeySetRefs = r.mKeySetRefsSnapshot.snapshot(); - mKeySetRefsSnapshot = new SnapshotCache.Sealed<>(); - mRenamedPackages.snapshot(r.mRenamedPackages); mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); mPendingDefaultBrowser.snapshot(r.mPendingDefaultBrowser); @@ -2755,7 +2727,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile // right time. invalidatePackageCache(); - mPastSignatures.clear(); + ArrayList<Signature> writtenSignatures = new ArrayList<>(); try (ResilientAtomicFile atomicFile = getSettingsFile()) { FileOutputStream str = null; @@ -2803,7 +2775,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile // load continue; } - writePackageLPr(serializer, pkg); + writePackageLPr(serializer, writtenSignatures, pkg); } for (final PackageSetting pkg : mDisabledSysPackages.values()) { @@ -2819,7 +2791,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile serializer.startTag(null, "shared-user"); serializer.attribute(null, ATTR_NAME, usr.name); serializer.attributeInt(null, "userId", usr.mAppId); - usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage()); + usr.signatures.writeXml(serializer, "sigs", writtenSignatures); serializer.endTag(null, "shared-user"); } @@ -2859,6 +2831,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } } } + //Debug.stopMethodTracing(); } @@ -3159,7 +3132,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile serializer.endTag(null, "updated-package"); } - void writePackageLPr(TypedXmlSerializer serializer, final PackageSetting pkg) + void writePackageLPr(TypedXmlSerializer serializer, ArrayList<Signature> writtenSignatures, + PackageSetting pkg) throws java.io.IOException { serializer.startTag(null, "package"); serializer.attribute(null, ATTR_NAME, pkg.getPackageName()); @@ -3259,11 +3233,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(), pkg.getUsesStaticLibrariesVersions()); - pkg.getSignatures().writeXml(serializer, "sigs", mPastSignatures.untrackedStorage()); + pkg.getSignatures().writeXml(serializer, "sigs", writtenSignatures); if (installSource.mInitiatingPackageSignatures != null) { installSource.mInitiatingPackageSignatures.writeXml( - serializer, "install-initiator-sigs", mPastSignatures.untrackedStorage()); + serializer, "install-initiator-sigs", writtenSignatures); } writeSigningKeySetLPr(serializer, pkg.getKeySetData()); @@ -3305,11 +3279,12 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile boolean readSettingsLPw(@NonNull Computer computer, @NonNull List<UserInfo> users, ArrayMap<String, Long> originalFirstInstallTimes) { mPendingPackages.clear(); - mPastSignatures.clear(); - mKeySetRefs.clear(); mInstallerPackages.clear(); originalFirstInstallTimes.clear(); + ArrayMap<Long, Integer> keySetRefs = new ArrayMap<>(); + ArrayList<Signature> readSignatures = new ArrayList<>(); + try (ResilientAtomicFile atomicFile = getSettingsFile()) { FileInputStream str = null; try { @@ -3346,13 +3321,14 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile String tagName = parser.getName(); if (tagName.equals("package")) { - readPackageLPw(parser, users, originalFirstInstallTimes); + readPackageLPw(parser, readSignatures, keySetRefs, users, + originalFirstInstallTimes); } else if (tagName.equals("permissions")) { mPermissions.readPermissions(parser); } else if (tagName.equals("permission-trees")) { mPermissions.readPermissionTrees(parser); } else if (tagName.equals("shared-user")) { - readSharedUserLPw(parser, users); + readSharedUserLPw(parser, readSignatures, users); } else if (tagName.equals("preferred-packages")) { // no longer used. } else if (tagName.equals("preferred-activities")) { @@ -3412,8 +3388,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { // No longer used. } else if (tagName.equals("keyset-settings")) { - mKeySetManagerService.readKeySetsLPw(parser, - mKeySetRefs.untrackedStorage()); + mKeySetManagerService.readKeySetsLPw(parser, keySetRefs); } else if (TAG_VERSION.equals(tagName)) { final String volumeUuid = XmlUtils.readStringAttribute(parser, ATTR_VOLUME_UUID); @@ -4007,7 +3982,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile private static final int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1 << 28; private static final int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1 << 30; - private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users, + private void readPackageLPw(TypedXmlPullParser parser, ArrayList<Signature> readSignatures, + ArrayMap<Long, Integer> keySetRefs, List<UserInfo> users, ArrayMap<String, Long> originalFirstInstallTimes) throws XmlPullParserException, IOException { String name = null; @@ -4282,8 +4258,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) { readEnabledComponentsLPw(packageSetting, parser, 0); } else if (tagName.equals("sigs")) { - packageSetting.getSignatures() - .readXml(parser,mPastSignatures.untrackedStorage()); + packageSetting.getSignatures().readXml(parser, readSignatures); } else if (tagName.equals(TAG_PERMISSIONS)) { final LegacyPermissionState legacyState; if (packageSetting.hasSharedUser()) { @@ -4300,11 +4275,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } } else if (tagName.equals("proper-signing-keyset")) { long id = parser.getAttributeLong(null, "identifier"); - Integer refCt = mKeySetRefs.get(id); + Integer refCt = keySetRefs.get(id); if (refCt != null) { - mKeySetRefs.put(id, refCt + 1); + keySetRefs.put(id, refCt + 1); } else { - mKeySetRefs.put(id, 1); + keySetRefs.put(id, 1); } packageSetting.getKeySetData().setProperSigningKeySet(id); } else if (tagName.equals("signing-keyset")) { @@ -4315,16 +4290,16 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } else if (tagName.equals("defined-keyset")) { long id = parser.getAttributeLong(null, "identifier"); String alias = parser.getAttributeValue(null, "alias"); - Integer refCt = mKeySetRefs.get(id); + Integer refCt = keySetRefs.get(id); if (refCt != null) { - mKeySetRefs.put(id, refCt + 1); + keySetRefs.put(id, refCt + 1); } else { - mKeySetRefs.put(id, 1); + keySetRefs.put(id, 1); } packageSetting.getKeySetData().addDefinedKeySet(id, alias); } else if (tagName.equals("install-initiator-sigs")) { final PackageSignatures signatures = new PackageSignatures(); - signatures.readXml(parser, mPastSignatures.untrackedStorage()); + signatures.readXml(parser, readSignatures); packageSetting.setInstallSource( packageSetting.getInstallSource() .setInitiatingPackageSignatures(signatures)); @@ -4497,7 +4472,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } } - private void readSharedUserLPw(TypedXmlPullParser parser, List<UserInfo> users) + private void readSharedUserLPw(TypedXmlPullParser parser, ArrayList<Signature> readSignatures, + List<UserInfo> users) throws XmlPullParserException, IOException { String name = null; int pkgFlags = 0; @@ -4539,7 +4515,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile String tagName = parser.getName(); if (tagName.equals("sigs")) { - su.signatures.readXml(parser, mPastSignatures.untrackedStorage()); + su.signatures.readXml(parser, readSignatures); } else if (tagName.equals("perms")) { readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users); } else { diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index 14db70e5f72e..23d0230a6080 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -288,6 +288,28 @@ public final class UserTypeFactory { * configuration. */ private static UserTypeDetails.Builder getDefaultTypeProfilePrivate() { + UserProperties.Builder userPropertiesBuilder = new UserProperties.Builder() + .setStartWithParent(true) + .setCredentialShareableWithParent(true) + .setAuthAlwaysRequiredToDisableQuietMode(true) + .setAllowStoppingUserWithDelayedLocking(true) + .setMediaSharedWithParent(false) + .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE) + .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE) + .setShowInQuietMode( + UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) + .setShowInSharingSurfaces( + UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE) + .setCrossProfileIntentFilterAccessControl( + UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM) + .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT) + .setCrossProfileContentSharingStrategy( + UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT); + if (android.multiuser.Flags.supportHidingProfiles()) { + userPropertiesBuilder.setProfileApiVisibility( + UserProperties.PROFILE_API_VISIBILITY_HIDDEN); + } + return new UserTypeDetails.Builder() .setName(USER_TYPE_PROFILE_PRIVATE) .setBaseType(FLAG_PROFILE) @@ -306,23 +328,7 @@ public final class UserTypeFactory { .setDarkThemeBadgeColors( R.color.white) .setDefaultRestrictions(getDefaultProfileRestrictions()) - .setDefaultUserProperties(new UserProperties.Builder() - .setStartWithParent(true) - .setCredentialShareableWithParent(true) - .setAuthAlwaysRequiredToDisableQuietMode(true) - .setAllowStoppingUserWithDelayedLocking(true) - .setMediaSharedWithParent(false) - .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE) - .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE) - .setShowInQuietMode( - UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) - .setShowInSharingSurfaces( - UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE) - .setCrossProfileIntentFilterAccessControl( - UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM) - .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT) - .setCrossProfileContentSharingStrategy( - UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)); + .setDefaultUserProperties(userPropertiesBuilder); } /** diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index d0fe9647618a..6ed2d3126455 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -421,6 +421,11 @@ public class PackageInfoUtils { if (ai.isArchived) { ai.nonLocalizedLabel = state.getArchiveState().getActivityInfos().get(0).getTitle(); } + if (!state.isInstalled() && !state.dataExists() + && android.content.pm.Flags.nullableDataDir()) { + // The data dir has been deleted + ai.dataDir = null; + } } @Nullable diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 09b19e6196a1..25e749f08782 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -138,6 +138,7 @@ import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.net.module.util.NetworkCapabilitiesUtils; +import com.android.server.power.optimization.Flags; import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import libcore.util.EmptyArray; @@ -185,7 +186,8 @@ public class BatteryStatsImpl extends BatteryStats { // TODO: remove "tcp" from network methods, since we measure total stats. // Current on-disk Parcel version. Must be updated when the format of the parcelable changes - public static final int VERSION = 214; + public static final int VERSION = + !Flags.disableSystemServicePowerAttr() ? 214 : 215; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -1753,7 +1755,9 @@ public class BatteryStatsImpl extends BatteryStats { mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, mClock); mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, mClock); mKernelWakelockReader = new KernelWakelockReader(); - mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + if (!Flags.disableSystemServicePowerAttr()) { + mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + } mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats(); mTmpRailStats = new RailStats(); } @@ -11459,7 +11463,7 @@ public class BatteryStatsImpl extends BatteryStats { @Override public BatteryStatsHistoryIterator iterateBatteryStatsHistory(long startTimeMs, long endTimeMs) { - return mHistory.copy().iterate(startTimeMs, endTimeMs); + return mHistory.iterate(startTimeMs, endTimeMs); } @Override @@ -11702,7 +11706,9 @@ public class BatteryStatsImpl extends BatteryStats { EnergyConsumerStats.resetIfNotNull(mGlobalEnergyConsumerStats); - resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); + if (!Flags.disableSystemServicePowerAttr()) { + resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); + } mNumAllUidCpuTimeReads = 0; mNumUidsRemoved = 0; @@ -13676,7 +13682,9 @@ public class BatteryStatsImpl extends BatteryStats { mKernelCpuSpeedReaders[i].readDelta(); } } - mSystemServerCpuThreadReader.readDelta(); + if (!Flags.disableSystemServicePowerAttr()) { + mSystemServerCpuThreadReader.readDelta(); + } return; } @@ -15696,23 +15704,25 @@ public class BatteryStatsImpl extends BatteryStats { } } - updateSystemServiceCallStats(); - if (mBinderThreadCpuTimesUs != null) { - pw.println("Per UID System server binder time in ms:"); - long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds(); - for (int i = 0; i < size; i++) { - int u = mUidStats.keyAt(i); - Uid uid = mUidStats.get(u); - double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage(); - long timeUs = 0; - for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) { - timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage; - } + if (!Flags.disableSystemServicePowerAttr()) { + updateSystemServiceCallStats(); + if (mBinderThreadCpuTimesUs != null) { + pw.println("Per UID System server binder time in ms:"); + long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds(); + for (int i = 0; i < size; i++) { + int u = mUidStats.keyAt(i); + Uid uid = mUidStats.get(u); + double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage(); + long timeUs = 0; + for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) { + timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage; + } - pw.print(" "); - pw.print(u); - pw.print(": "); - pw.println(timeUs / 1000); + pw.print(" "); + pw.print(u); + pw.print(": "); + pw.println(timeUs / 1000); + } } } } @@ -16428,8 +16438,10 @@ public class BatteryStatsImpl extends BatteryStats { } } - mBinderThreadCpuTimesUs = - LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase); + if (!Flags.disableSystemServicePowerAttr()) { + mBinderThreadCpuTimesUs = + LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase); + } } /** @@ -16973,7 +16985,9 @@ public class BatteryStatsImpl extends BatteryStats { } } - LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs); + if (!Flags.disableSystemServicePowerAttr()) { + LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs); + } } @GuardedBy("this") @@ -16985,7 +16999,9 @@ public class BatteryStatsImpl extends BatteryStats { // if we had originally pulled a time before the RTC was set. getStartClockTime(); - updateSystemServiceCallStats(); + if (!Flags.disableSystemServicePowerAttr()) { + updateSystemServiceCallStats(); + } } @GuardedBy("this") diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index c3221e4929bd..30b80ae781ff 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -96,11 +96,13 @@ public class BatteryUsageStatsProvider { mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile)); mPowerCalculators.add(new UserPowerCalculator()); - // It is important that SystemServicePowerCalculator be applied last, - // because it re-attributes some of the power estimated by the other - // calculators. - mPowerCalculators.add( - new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile)); + if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { + // It is important that SystemServicePowerCalculator be applied last, + // because it re-attributes some of the power estimated by the other + // calculators. + mPowerCalculators.add( + new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile)); + } } } return mPowerCalculators; diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java index cd3db36662d6..ba4c127ac3d0 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java @@ -74,8 +74,7 @@ public class PowerStatsAggregator { boolean clockUpdateAdded = false; long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED; long lastTime = 0; - try (BatteryStatsHistoryIterator iterator = - mHistory.copy().iterate(startTimeMs, endTimeMs)) { + try (BatteryStatsHistoryIterator iterator = mHistory.iterate(startTimeMs, endTimeMs)) { while (iterator.hasNext()) { BatteryStats.HistoryItem item = iterator.next(); diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig index 0f135715ebc3..65466461c82e 100644 --- a/services/core/java/com/android/server/power/stats/flags.aconfig +++ b/services/core/java/com/android/server/power/stats/flags.aconfig @@ -13,3 +13,11 @@ flag { description: "Feature flag for streamlined battery stats" bug: "285646152" } + +flag { + name: "disable_system_service_power_attr" + namespace: "backstage_power" + description: "Deprecation of system service power re-attribution" + bug: "311793616" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index a4dbce6b2631..3c0547edbd92 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -527,6 +527,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]"); synchronized (sRequestLock) { if (!setupOrClearBcb(true, command)) { + Slog.e(TAG, "rebootRecoveryWithCommand failed to setup BCB"); return; } diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java index 141d4dcf77d0..9ee9b149529e 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java @@ -48,6 +48,8 @@ public class RecoverySystemShellCommand extends ShellCommand { return isLskfCaptured(); case "reboot-and-apply": return rebootAndApply(); + case "wipe": + return wipe(); default: return handleDefaultCommands(cmd); } @@ -58,6 +60,18 @@ public class RecoverySystemShellCommand extends ShellCommand { } } + private int wipe() throws RemoteException { + PrintWriter pw = getOutPrintWriter(); + String newFsType = getNextArg(); + String command = "--wipe_data"; + if (newFsType != null && !newFsType.isEmpty()) { + command += "\n--reformat_data=" + newFsType; + } + pw.println("Rebooting into recovery with " + command.replaceAll("\n", " ")); + mService.rebootRecoveryWithCommand(command); + return 0; + } + private int requestLskf() throws RemoteException { String packageName = getNextArgRequired(); boolean success = mService.requestLskf(packageName, null); @@ -104,5 +118,6 @@ public class RecoverySystemShellCommand extends ShellCommand { pw.println(" clear-lskf"); pw.println(" is-lskf-captured <package_name>"); pw.println(" reboot-and-apply <package_name> <reason>"); + pw.println(" wipe <new filesystem type ext4/f2fs>"); } } diff --git a/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java b/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java index 6677e7eb320c..152623090314 100644 --- a/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java +++ b/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java @@ -36,10 +36,12 @@ import android.content.IntentFilter; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; +import android.location.flags.Flags; import android.os.Bundle; import android.os.Process; import android.os.UserHandle; import android.util.Log; +import android.util.TypedValue; import com.android.internal.util.Preconditions; import com.android.server.FgThread; @@ -70,6 +72,10 @@ public final class CurrentUserServiceSupplier extends BroadcastReceiver implemen private static final String EXTRA_SERVICE_VERSION = "serviceVersion"; private static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser"; + // a package value that will never match against any package (we can't use null since this will + // match against any package). + private static final String NO_MATCH_PACKAGE = ""; + private static final Comparator<BoundServiceInfo> sBoundServiceInfoComparator = (o1, o2) -> { if (o1 == o2) { return 0; @@ -196,7 +202,19 @@ public final class CurrentUserServiceSupplier extends BroadcastReceiver implemen Resources resources = context.getResources(); boolean enableOverlay = resources.getBoolean(enableOverlayResId); if (!enableOverlay) { - return resources.getString(nonOverlayPackageResId); + if (Flags.fixServiceWatcher()) { + // we don't use getText() or similar because it won't return null values + TypedValue out = new TypedValue(); + resources.getValue(nonOverlayPackageResId, out, true); + CharSequence explicitPackage = out.coerceToString(); + if (explicitPackage == null) { + return NO_MATCH_PACKAGE; + } else { + return explicitPackage.toString(); + } + } else { + return resources.getString(nonOverlayPackageResId); + } } else { return null; } @@ -233,6 +251,10 @@ public final class CurrentUserServiceSupplier extends BroadcastReceiver implemen @Override public boolean hasMatchingService() { + if (Flags.fixServiceWatcher() && NO_MATCH_PACKAGE.equals(mIntent.getPackage())) { + return false; + } + int intentQueryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; if (mMatchSystemAppsOnly) { intentQueryFlags |= MATCH_SYSTEM_ONLY; @@ -268,6 +290,10 @@ public final class CurrentUserServiceSupplier extends BroadcastReceiver implemen @Override public BoundServiceInfo getServiceInfo() { + if (Flags.fixServiceWatcher() && NO_MATCH_PACKAGE.equals(mIntent.getPackage())) { + return null; + } + BoundServiceInfo bestServiceInfo = null; // only allow services in the correct direct boot state to match diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 285bcc328c0c..0ffd002197c4 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2058,7 +2058,8 @@ public class StatsPullAtomService extends SystemService { } private void registerCpuCyclesPerThreadGroupCluster() { - if (KernelCpuBpfTracking.isSupported()) { + if (KernelCpuBpfTracking.isSupported() + && !com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER; PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[]{3, 4}) @@ -2073,6 +2074,10 @@ public class StatsPullAtomService extends SystemService { } int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) { + if (com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { + return StatsManager.PULL_SKIP; + } + SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class) .getSystemServiceCpuThreadTimes(); if (times == null) { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index e5a8a6dd2a3a..dbf777f48e35 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -83,7 +83,6 @@ import com.android.internal.infra.AndroidFuture; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.SystemService; -import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -160,7 +159,6 @@ public class TrustManagerService extends SystemService { private final ActivityManager mActivityManager; private FingerprintManager mFingerprintManager; private FaceManager mFaceManager; - private VirtualDeviceManagerInternal mVirtualDeviceManager; private enum TrustState { // UNTRUSTED means that TrustManagerService is currently *not* giving permission for the diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java index b6d0ca19d484..eacd3f8d4d86 100644 --- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java @@ -4152,6 +4152,25 @@ public class TvInteractiveAppManagerService extends SystemService { } @Override + public void onRequestSigning2(String id, String algorithm, String host, + int port, byte[] data) { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onRequestSigning"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onRequestSigning2( + id, algorithm, host, port, data, mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onRequestSigning", e); + } + } + } + + @Override public void onRequestCertificate(String host, int port) { synchronized (mLock) { if (DEBUG) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index f2d9bf810b53..fa8c35a6953e 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1540,16 +1540,12 @@ final class AccessibilityController { private static final boolean DEBUG = false; - private final List<AccessibilityWindow> mTempA11yWindows = new ArrayList<>(); - private final Set<IBinder> mTempBinderSet = new ArraySet<>(); private final Point mTempPoint = new Point(); private final Region mTempRegion = new Region(); - private final Region mTempRegion1 = new Region(); - private final Region mTempRegion2 = new Region(); private final WindowManagerService mService; @@ -1617,7 +1613,8 @@ final class AccessibilityController { Slog.i(LOG_TAG, "computeChangedWindows()"); } - List<WindowInfo> windows = new ArrayList<>(); + final List<WindowInfo> windows = new ArrayList<>(); + final List<AccessibilityWindow> visibleWindows = new ArrayList<>(); final int topFocusedDisplayId; IBinder topFocusedWindowToken = null; @@ -1652,7 +1649,6 @@ final class AccessibilityController { Region unaccountedSpace = mTempRegion; unaccountedSpace.set(0, 0, screenWidth, screenHeight); - final List<AccessibilityWindow> visibleWindows = mTempA11yWindows; mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked( mDisplayId, visibleWindows); Set<IBinder> addedWindows = mTempBinderSet; @@ -1709,7 +1705,6 @@ final class AccessibilityController { } } - visibleWindows.clear(); addedWindows.clear(); // Gets the top focused display Id and window token for supporting multi-display. @@ -1720,7 +1715,9 @@ final class AccessibilityController { topFocusedWindowToken, windows); // Recycle the windows as we do not need them. - clearAndRecycleWindows(windows); + for (final AccessibilityWindowsPopulator.AccessibilityWindow window : visibleWindows) { + window.getWindowInfo().recycle(); + } mInitialized = true; } @@ -1802,13 +1799,6 @@ final class AccessibilityController { tokenOut.add(window.token); } - private static void clearAndRecycleWindows(List<WindowInfo> windows) { - final int windowCount = windows.size(); - for (int i = windowCount - 1; i >= 0; i--) { - windows.remove(i).recycle(); - } - } - private static boolean isReportedWindowType(int windowType) { return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 03d55d9edfda..b1d04c9ddb16 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -545,9 +545,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean launchFailed; // set if a launched failed, to abort on 2nd try boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? - boolean deferRelaunchUntilPaused; // relaunch of activity is being deferred until pause is - // completed - boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch int configChangeFlags; // which config values have changed private boolean keysPaused; // has key dispatching been paused for it? int launchMode; // the launch mode activity attribute. @@ -1277,10 +1274,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mDeferHidingClient) { pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient); } - if (deferRelaunchUntilPaused || configChangeFlags != 0) { - pw.print(prefix); pw.print("deferRelaunchUntilPaused="); - pw.print(deferRelaunchUntilPaused); - pw.print(" configChangeFlags="); + if (configChangeFlags != 0) { + pw.print(prefix); pw.print(" configChangeFlags="); pw.println(Integer.toHexString(configChangeFlags)); } if (mServiceConnectionsHolder != null) { @@ -2137,7 +2132,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A launchFailed = false; delayedResume = false; finishing = false; - deferRelaunchUntilPaused = false; keysPaused = false; inHistory = false; nowVisible = false; @@ -4096,8 +4090,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Clean up the splash screen if it was still displayed. cleanUpSplashScreen(); - deferRelaunchUntilPaused = false; - if (setState) { setState(DESTROYED, "cleanUp"); if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + this); @@ -6481,9 +6473,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAppStopped = true; ProtoLog.v(WM_DEBUG_STATES, "Stop failed; moving to STOPPED: %s", this); setState(STOPPED, "stopIfPossible"); - if (deferRelaunchUntilPaused) { - destroyImmediately("stop-except"); - } } } @@ -6539,12 +6528,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (finishing) { abortAndClearOptionsAnimation(); } else { - if (deferRelaunchUntilPaused) { - destroyImmediately("stop-config"); - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } else { - mAtmService.updatePreviousProcess(this); - } + mAtmService.updatePreviousProcess(this); } mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */); } @@ -9724,23 +9708,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { mRelaunchReason = RELAUNCH_REASON_NONE; } - if (mState == PAUSING) { - // A little annoying: we are waiting for this activity to finish pausing. Let's not - // do anything now, but just flag that it needs to be restarted when done pausing. - ProtoLog.v(WM_DEBUG_CONFIGURATION, - "Config is skipping already pausing %s", this); - deferRelaunchUntilPaused = true; - preserveWindowOnDeferredRelaunch = preserveWindow; - return true; - } else { - ProtoLog.v(WM_DEBUG_CONFIGURATION, "Config is relaunching %s", - this); - if (!mVisibleRequested) { - ProtoLog.v(WM_DEBUG_STATES, "Config is relaunching invisible " - + "activity %s called by %s", this, Debug.getCallers(4)); - } - relaunchActivityLocked(preserveWindow); + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Config is relaunching %s", this); + if (!mVisibleRequested) { + ProtoLog.v(WM_DEBUG_STATES, "Config is relaunching invisible " + + "activity %s called by %s", this, Debug.getCallers(4)); } + relaunchActivityLocked(preserveWindow); // All done... tell the caller we weren't able to keep this activity around. return false; @@ -9958,8 +9931,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTaskSupervisor.mStoppingActivities.remove(this); configChangeFlags = 0; - deferRelaunchUntilPaused = false; - preserveWindowOnDeferredRelaunch = false; } /** diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java index a692167bbbf9..1f013b913283 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java @@ -31,8 +31,6 @@ import android.util.SparseArray; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.LocalServices; -import com.android.server.pm.UserManagerInternal; import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; import com.android.window.flags.Flags; @@ -163,18 +161,12 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord private void cleanUpUserFiles(int userId) { synchronized (mSnapshotPersistQueue.getLock()) { mSnapshotPersistQueue.sendToQueueLocked( - new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) { - @Override - boolean isReady() { - final UserManagerInternal mUserManagerInternal = - LocalServices.getService(UserManagerInternal.class); - return mUserManagerInternal.isUserUnlocked(userId); - } + new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider, userId) { @Override void write() { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cleanUpUserFiles"); - final File file = mPersistInfoProvider.getDirectory(userId); + final File file = mPersistInfoProvider.getDirectory(mUserId); if (file.exists()) { final File[] contents = file.listFiles(); if (contents != null) { @@ -263,15 +255,13 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord class LoadActivitySnapshotItem extends SnapshotPersistQueue.WriteQueueItem { private final int mCode; - private final int mUserId; private final ActivityRecord[] mActivities; LoadActivitySnapshotItem(@NonNull ActivityRecord[] activities, int code, int userId, @NonNull PersistInfoProvider persistInfoProvider) { - super(persistInfoProvider); + super(persistInfoProvider, userId); mActivities = activities; mCode = code; - mUserId = userId; } @Override @@ -305,6 +295,11 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord return mCode == other.mCode && mUserId == other.mUserId && mPersistInfoProvider == other.mPersistInfoProvider; } + + @Override + public String toString() { + return "LoadActivitySnapshotItem{code=" + mCode + ", UserId=" + mUserId + "}"; + } } void loadActivitySnapshot() { @@ -714,24 +709,9 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } removeTargets.add(usf); } - removeSnapshotFiles(removeTargets); - } - - private void removeSnapshotFiles(@NonNull ArrayList<UserSavedFile> files) { - synchronized (mSnapshotPersistQueue.getLock()) { - mSnapshotPersistQueue.sendToQueueLocked( - new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) { - @Override - void write() { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activity_remove_files"); - for (int i = files.size() - 1; i >= 0; --i) { - final UserSavedFile usf = files.get(i); - mSnapshotPersistQueue.deleteSnapshot( - usf.mFileId, usf.mUserId, mPersistInfoProvider); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - }); + for (int i = removeTargets.size() - 1; i >= 0; --i) { + final UserSavedFile usf = removeTargets.get(i); + mPersister.removeSnapshot(usf.mFileId, usf.mUserId); } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 83ccbdc1a4d1..13f6a5f1a27b 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1527,6 +1527,12 @@ class BackNavigationController { setLaunchBehind(visibleOpenActivities[i]); } } + // Force update mLastSurfaceShowing for opening activity and its task. + if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()) { + for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { + WindowContainer.enforceSurfaceVisible(visibleOpenActivities[i]); + } + } } @Nullable Runnable build() { diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 4681396affa5..688a3b5d647c 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -719,14 +719,6 @@ public class BackgroundActivityStartController { && !state.realCallerExplicitOptOut(); if (callerCanAllow) { // Allowed before V by creator - if (state.mBalAllowedByPiCreatorWithHardening.allowsBackgroundActivityStarts()) { - // Will be allowed even with BAL hardening. - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed by caller. " - + state.dump()); - } - return allowBasedOnCaller(state); - } if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) { Slog.wtf(TAG, "With Android 15 BAL hardening this activity start may be blocked" + " if the PI creator upgrades target_sdk to 35+! " @@ -997,7 +989,7 @@ public class BackgroundActivityStartController { BalVerdict balAllowedForUid = proc.areBackgroundActivityStartsAllowed( state.mAppSwitchState); if (balAllowedForUid.allows()) { - return balAllowedForCaller.withProcessInfo("process", proc); + return balAllowedForUid.withProcessInfo("process", proc); } } } diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java index 0f9e5b0b2892..7052982d3203 100644 --- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java +++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java @@ -183,9 +183,6 @@ public class DeferredDisplayUpdater implements DisplayUpdater { getCurrentDisplayChange(fromRotation, startBounds); mDisplayContent.mTransitionController.requestStartTransition(transition, /* startTask= */ null, /* remoteTransition= */ null, displayChange); - mDisplayContent.mTransitionController.setDisplaySyncMethod(displayChange, - mDisplayContent); - transition.setAllReady(); } }); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e7ecf520425d..82dbf8d4242b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -160,6 +160,7 @@ import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELD import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; import static com.android.server.wm.utils.RegionUtils.forEachRectReverse; import static com.android.server.wm.utils.RegionUtils.rectListToRegion; +import static com.android.window.flags.Flags.deferDisplayUpdates; import static com.android.window.flags.Flags.explicitRefreshRateHints; import android.annotation.IntDef; @@ -174,7 +175,6 @@ import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Bitmap; import android.graphics.ColorSpace; import android.graphics.Insets; import android.graphics.Matrix; @@ -246,7 +246,6 @@ import android.view.inputmethod.ImeTracker; import android.window.DisplayWindowPolicyController; import android.window.IDisplayAreaOrganizer; import android.window.ScreenCapture; -import android.window.ScreenCapture.SynchronousScreenCaptureListener; import android.window.SystemPerformanceHinter; import android.window.TransitionRequestInfo; @@ -276,7 +275,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import static com.android.window.flags.Flags.deferDisplayUpdates; /** * Utility class for keeping track of the WindowStates and other pertinent contents of a @@ -1636,12 +1634,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (configChanged) { mWaitingForConfig = true; if (mTransitionController.isShellTransitionsEnabled()) { - final TransitionRequestInfo.DisplayChange change = - mTransitionController.isCollecting() + final Rect startBounds = currentDisplayConfig.windowConfiguration.getBounds(); + final Rect endBounds = mTmpConfiguration.windowConfiguration.getBounds(); + final Transition transition = mTransitionController.getCollectingTransition(); + final TransitionRequestInfo.DisplayChange change = transition != null ? null : new TransitionRequestInfo.DisplayChange(mDisplayId); if (change != null) { - change.setStartAbsBounds(currentDisplayConfig.windowConfiguration.getBounds()); - change.setEndAbsBounds(mTmpConfiguration.windowConfiguration.getBounds()); + change.setStartAbsBounds(startBounds); + change.setEndAbsBounds(endBounds); + } else { + transition.setKnownConfigChanges(this, changes); + // A collecting transition is existed. The sync method must be set before + // collecting this display, so WindowState#prepareSync can use the sync method. + mTransitionController.setDisplaySyncMethod(startBounds, endBounds, this); } requestChangeTransitionIfNeeded(changes, change); } else if (mLastHasContent) { @@ -5207,10 +5212,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** - * Takes a snapshot of the display. In landscape mode this grabs the whole screen. - * In portrait mode, it grabs the full screenshot. + * Creates a LayerCaptureArgs object to represent the entire DisplayContent */ - Bitmap screenshotDisplayLocked() { + ScreenCapture.LayerCaptureArgs getLayerCaptureArgs() { if (!mWmService.mPolicy.isScreenOn()) { if (DEBUG_SCREENSHOT) { Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); @@ -5218,24 +5222,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return null; } - SynchronousScreenCaptureListener syncScreenCapture = - ScreenCapture.createSyncCaptureListener(); - getBounds(mTmpRect); mTmpRect.offsetTo(0, 0); - ScreenCapture.LayerCaptureArgs args = - new ScreenCapture.LayerCaptureArgs.Builder(getSurfaceControl()) - .setSourceCrop(mTmpRect).build(); - - ScreenCapture.captureLayers(args, syncScreenCapture); - - final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = - syncScreenCapture.getBuffer(); - final Bitmap bitmap = screenshotBuffer == null ? null : screenshotBuffer.asBitmap(); - if (bitmap == null) { - Slog.w(TAG_WM, "Failed to take screenshot"); - } - return bitmap; + return new ScreenCapture.LayerCaptureArgs.Builder(getSurfaceControl()) + .setSourceCrop(mTmpRect).build(); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index e2bc59bb6550..a7bbc25d0bb1 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1913,12 +1913,9 @@ public class DisplayPolicy { */ final Rect mConfigFrame = new Rect(); - /** The count of insets sources when calculating this info. */ - int mLastInsetsSourceCount; - private boolean mNeedUpdate = true; - void update(DisplayContent dc, int rotation, int w, int h) { + InsetsState update(DisplayContent dc, int rotation, int w, int h) { final DisplayFrames df = new DisplayFrames(); dc.updateDisplayFrames(df, rotation, w, h); dc.getDisplayPolicy().simulateLayoutDisplay(df); @@ -1935,8 +1932,8 @@ public class DisplayPolicy { mNonDecorFrame.inset(mNonDecorInsets); mConfigFrame.set(displayFrame); mConfigFrame.inset(mConfigInsets); - mLastInsetsSourceCount = dc.getDisplayPolicy().mInsetsSourceWindowsExceptIme.size(); mNeedUpdate = false; + return insetsState; } void set(Info other) { @@ -1944,7 +1941,6 @@ public class DisplayPolicy { mConfigInsets.set(other.mConfigInsets); mNonDecorFrame.set(other.mNonDecorFrame); mConfigFrame.set(other.mConfigFrame); - mLastInsetsSourceCount = other.mLastInsetsSourceCount; mNeedUpdate = false; } @@ -1997,6 +1993,29 @@ public class DisplayPolicy { } } + static boolean hasInsetsFrameDiff(InsetsState s1, InsetsState s2, int insetsTypes) { + int insetsCount1 = 0; + for (int i = s1.sourceSize() - 1; i >= 0; i--) { + final InsetsSource source1 = s1.sourceAt(i); + if ((source1.getType() & insetsTypes) == 0) { + continue; + } + insetsCount1++; + final InsetsSource source2 = s2.peekSource(source1.getId()); + if (source2 == null || !source2.getFrame().equals(source1.getFrame())) { + return true; + } + } + int insetsCount2 = 0; + for (int i = s2.sourceSize() - 1; i >= 0; i--) { + final InsetsSource source2 = s2.sourceAt(i); + if ((source2.getType() & insetsTypes) != 0) { + insetsCount2++; + } + } + return insetsCount1 != insetsCount2; + } + private static class Cache { /** * If {@link #mPreserveId} is this value, it is in the middle of updating display @@ -2031,12 +2050,14 @@ public class DisplayPolicy { final int dw = displayFrames.mWidth; final int dh = displayFrames.mHeight; final DecorInsets.Info newInfo = mDecorInsets.mTmpInfo; - newInfo.update(mDisplayContent, rotation, dw, dh); + final InsetsState newInsetsState = newInfo.update(mDisplayContent, rotation, dw, dh); final DecorInsets.Info currentInfo = getDecorInsetsInfo(rotation, dw, dh); if (newInfo.mConfigFrame.equals(currentInfo.mConfigFrame)) { // Even if the config frame is not changed in current rotation, it may change the - // insets in other rotations if the source count is changed. - if (newInfo.mLastInsetsSourceCount != currentInfo.mLastInsetsSourceCount) { + // insets in other rotations if the frame of insets source is changed. + final InsetsState currentInsetsState = mDisplayContent.mDisplayFrames.mInsetsState; + if (DecorInsets.hasInsetsFrameDiff( + newInsetsState, currentInsetsState, mService.mConfigTypes)) { for (int i = mDecorInsets.mInfoForRotation.length - 1; i >= 0; i--) { if (i != rotation) { final boolean flipSize = (i + rotation) % 2 == 1; diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java index 5f488b769885..bdb45884887c 100644 --- a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java +++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java @@ -97,7 +97,7 @@ public class ScreenRecordingCallbackController { mRecordedWC = (WindowContainer) mWms.mRoot.getDefaultDisplay(); } else { mRecordedWC = mWms.mRoot.getActivity(activity -> activity.mLaunchCookie - == mediaProjectionInfo.getLaunchCookie()).getTask(); + == mediaProjectionInfo.getLaunchCookie().binder).getTask(); } } diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index 3014f979aa70..cb388efdae89 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -211,5 +211,6 @@ class SnapshotController { void dump(PrintWriter pw, String prefix) { mTaskSnapshotController.dump(pw, prefix); mActivitySnapshotController.dump(pw, prefix); + mSnapshotPersistQueue.dump(pw, prefix); } } diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java index e4379b5343f3..357897127f3a 100644 --- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -41,6 +41,7 @@ import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayDeque; /** @@ -155,7 +156,9 @@ class SnapshotPersistQueue { void deleteSnapshot(int index, int userId, PersistInfoProvider provider) { final File protoFile = provider.getProtoFile(index, userId); final File bitmapLowResFile = provider.getLowResolutionBitmapFile(index, userId); - protoFile.delete(); + if (protoFile.exists()) { + protoFile.delete(); + } if (bitmapLowResFile.exists()) { bitmapLowResFile.delete(); } @@ -177,7 +180,7 @@ class SnapshotPersistQueue { } else { next = mWriteQueue.poll(); if (next != null) { - if (next.isReady()) { + if (next.isReady(mUserManagerInternal)) { isReadyToWrite = true; next.onDequeuedLocked(); } else { @@ -210,14 +213,16 @@ class SnapshotPersistQueue { abstract static class WriteQueueItem { protected final PersistInfoProvider mPersistInfoProvider; - WriteQueueItem(@NonNull PersistInfoProvider persistInfoProvider) { + protected final int mUserId; + WriteQueueItem(@NonNull PersistInfoProvider persistInfoProvider, int userId) { mPersistInfoProvider = persistInfoProvider; + mUserId = userId; } /** * @return {@code true} if item is ready to have {@link WriteQueueItem#write} called */ - boolean isReady() { - return true; + boolean isReady(UserManagerInternal userManager) { + return userManager.isUserUnlocked(mUserId); } abstract void write(); @@ -242,14 +247,12 @@ class SnapshotPersistQueue { class StoreWriteQueueItem extends WriteQueueItem { private final int mId; - private final int mUserId; private final TaskSnapshot mSnapshot; StoreWriteQueueItem(int id, int userId, TaskSnapshot snapshot, PersistInfoProvider provider) { - super(provider); + super(provider, userId); mId = id; - mUserId = userId; mSnapshot = snapshot; } @@ -268,11 +271,6 @@ class SnapshotPersistQueue { } @Override - boolean isReady() { - return mUserManagerInternal.isUserUnlocked(mUserId); - } - - @Override void write() { if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "StoreWriteQueueItem#" + mId); @@ -391,6 +389,11 @@ class SnapshotPersistQueue { return mId == other.mId && mUserId == other.mUserId && mPersistInfoProvider == other.mPersistInfoProvider; } + + @Override + public String toString() { + return "StoreWriteQueueItem{ID=" + mId + ", UserId=" + mUserId + "}"; + } } DeleteWriteQueueItem createDeleteWriteQueueItem(int id, int userId, @@ -400,12 +403,10 @@ class SnapshotPersistQueue { private class DeleteWriteQueueItem extends WriteQueueItem { private final int mId; - private final int mUserId; DeleteWriteQueueItem(int id, int userId, PersistInfoProvider provider) { - super(provider); + super(provider, userId); mId = id; - mUserId = userId; } @Override @@ -414,5 +415,24 @@ class SnapshotPersistQueue { deleteSnapshot(mId, mUserId, mPersistInfoProvider); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + + @Override + public String toString() { + return "DeleteWriteQueueItem{ID=" + mId + ", UserId=" + mUserId + "}"; + } + } + + void dump(PrintWriter pw, String prefix) { + final WriteQueueItem[] items; + synchronized (mLock) { + items = mWriteQueue.toArray(new WriteQueueItem[0]); + } + if (items.length == 0) { + return; + } + pw.println(prefix + "PersistQueue contains:"); + for (int i = items.length - 1; i >= 0; --i) { + pw.println(prefix + " " + items[i] + ""); + } } } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index b2b547e7d9e5..838ce86515cd 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.Manifest.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE; import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; @@ -725,6 +726,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { // TaskFragment to have bounds outside of the parent bounds. return false; } + if (hasEmbedAnyAppInUntrustedModePermission(mTaskFragmentOrganizerUid)) { + return true; + } return (a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING) == FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING; } @@ -796,6 +800,15 @@ class TaskFragment extends WindowContainer<WindowContainer> { } /** + * Checks if a particular app uid has the {@link EMBED_ANY_APP_IN_UNTRUSTED_MODE} permission. + */ + private static boolean hasEmbedAnyAppInUntrustedModePermission(int uid) { + return Flags.untrustedEmbeddingAnyAppPermission() + && checkPermission(EMBED_ANY_APP_IN_UNTRUSTED_MODE, + PermissionChecker.PID_UNKNOWN, uid) == PackageManager.PERMISSION_GRANTED; + } + + /** * Checks if all activities in the task fragment are embedded as fully trusted. * @see #isFullyTrustedEmbedding(ActivityRecord, int) * @param uid uid of the TaskFragment organizer. @@ -1902,11 +1915,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s " + "wasStopping=%b visibleRequested=%b", prev, wasStopping, prev.isVisibleRequested()); - if (prev.deferRelaunchUntilPaused) { - // Complete the deferred relaunch that was waiting for pause to complete. - ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev); - prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch); - } else if (wasStopping) { + if (wasStopping) { // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. // We don't need to schedule another stop, we only need to let it happen. diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 4e7a9bd2881a..f4e995719b6d 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -303,8 +303,15 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } else { if (DEBUG) appendLog("non-freeform-task-display-area"); } + final boolean isUpdatingExistingTaskWindowingMode = task != null + && task.getRequestedOverrideWindowingMode() != WINDOWING_MODE_UNDEFINED + && launchMode != task.getRequestedOverrideWindowingMode(); + if (DEBUG && isUpdatingExistingTaskWindowingMode) { + appendLog("updating-existing-task-windowing-mode"); + } // If launch mode matches display windowing mode, let it inherit from display. outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode() + && !isUpdatingExistingTaskWindowingMode ? WINDOWING_MODE_UNDEFINED : launchMode; if (phase == PHASE_WINDOWING_MODE) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 233daadfc496..87be74ae1dd9 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -19,11 +19,13 @@ package com.android.server.wm; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import android.os.Trace; +import android.os.UserHandle; import android.util.ArraySet; import android.window.TaskSnapshot; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.UserManagerInternal; import java.io.File; import java.util.Arrays; @@ -84,6 +86,9 @@ class TaskSnapshotPersister extends BaseAppSnapshotPersister { * model. */ void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) { + if (runningUserIds.length == 0) { + return; + } synchronized (mLock) { mPersistedTaskIdsSinceLastRemoveObsolete.clear(); mSnapshotPersistQueue.sendToQueueLocked(new RemoveObsoleteFilesQueueItem( @@ -99,12 +104,22 @@ class TaskSnapshotPersister extends BaseAppSnapshotPersister { @VisibleForTesting RemoveObsoleteFilesQueueItem(ArraySet<Integer> persistentTaskIds, int[] runningUserIds, PersistInfoProvider provider) { - super(provider); + super(provider, runningUserIds.length > 0 ? runningUserIds[0] : UserHandle.USER_SYSTEM); mPersistentTaskIds = new ArraySet<>(persistentTaskIds); mRunningUserIds = Arrays.copyOf(runningUserIds, runningUserIds.length); } @Override + boolean isReady(UserManagerInternal userManagerInternal) { + for (int i = mRunningUserIds.length - 1; i >= 0; --i) { + if (!userManagerInternal.isUserUnlocked(mRunningUserIds[i])) { + return false; + } + } + return true; + } + + @Override void write() { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RemoveObsoleteFilesQueueItem"); final ArraySet<Integer> newPersistedTaskIds; diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index d7b4a399514d..70775530d0e2 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -640,11 +640,16 @@ class TransitionController { } /** Sets the sync method for the display change. */ - void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange, + private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange, @NonNull DisplayContent displayContent) { final Rect startBounds = displayChange.getStartAbsBounds(); final Rect endBounds = displayChange.getEndAbsBounds(); if (startBounds == null || endBounds == null) return; + setDisplaySyncMethod(startBounds, endBounds, displayContent); + } + + void setDisplaySyncMethod(@NonNull Rect startBounds, @NonNull Rect endBounds, + @NonNull DisplayContent displayContent) { final int startWidth = startBounds.width(); final int startHeight = startBounds.height(); final int endWidth = endBounds.width(); @@ -989,39 +994,18 @@ class TransitionController { Slog.e(TAG, "Set visible without transition " + wc + " playing=" + isPlaying + " caller=" + caller); if (!isPlaying) { - enforceSurfaceVisible(wc); + WindowContainer.enforceSurfaceVisible(wc); return; } // Update surface visibility after the playing transitions are finished, so the last // visibility won't be replaced by the finish transaction of transition. mStateValidators.add(() -> { if (wc.isVisibleRequested()) { - enforceSurfaceVisible(wc); + WindowContainer.enforceSurfaceVisible(wc); } }); } - private void enforceSurfaceVisible(WindowContainer<?> wc) { - if (wc.mSurfaceControl == null) return; - wc.getSyncTransaction().show(wc.mSurfaceControl); - final ActivityRecord ar = wc.asActivityRecord(); - if (ar != null) { - ar.mLastSurfaceShowing = true; - } - // Force showing the parents because they may be hidden by previous transition. - for (WindowContainer<?> p = wc.getParent(); p != null && p != wc.mDisplayContent; - p = p.getParent()) { - if (p.mSurfaceControl != null) { - p.getSyncTransaction().show(p.mSurfaceControl); - final Task task = p.asTask(); - if (task != null) { - task.mLastSurfaceShowing = true; - } - } - } - wc.scheduleAnimation(); - } - /** * Called when the transition has a complete set of participants for its operation. In other * words, it is when the transition is "ready" but is still waiting for participants to draw. diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 399815b70a06..6949a874b533 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -279,13 +279,14 @@ class WallpaperController { return null; } Point largestDisplaySize = new Point(); + float largestWidth = 0; List<DisplayInfo> possibleDisplayInfo = mService.getPossibleDisplayInfoLocked(DEFAULT_DISPLAY); for (int i = 0; i < possibleDisplayInfo.size(); i++) { DisplayInfo displayInfo = possibleDisplayInfo.get(i); - if (displayInfo.type == Display.TYPE_INTERNAL - && Math.max(displayInfo.logicalWidth, displayInfo.logicalHeight) - > Math.max(largestDisplaySize.x, largestDisplaySize.y)) { + float width = (float) displayInfo.logicalWidth / displayInfo.physicalXDpi; + if (displayInfo.type == Display.TYPE_INTERNAL && width > largestWidth) { + largestWidth = width; largestDisplaySize.set(displayInfo.logicalWidth, displayInfo.logicalHeight); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 286182eedf44..2d2857aba781 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3625,6 +3625,29 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return mSurfaceControl.getHeight(); } + static void enforceSurfaceVisible(@NonNull WindowContainer<?> wc) { + if (wc.mSurfaceControl == null) { + return; + } + wc.getSyncTransaction().show(wc.mSurfaceControl); + final ActivityRecord ar = wc.asActivityRecord(); + if (ar != null) { + ar.mLastSurfaceShowing = true; + } + // Force showing the parents because they may be hidden by previous transition. + for (WindowContainer<?> p = wc.getParent(); p != null && p != wc.mDisplayContent; + p = p.getParent()) { + if (p.mSurfaceControl != null) { + p.getSyncTransaction().show(p.mSurfaceControl); + final Task task = p.asTask(); + if (task != null) { + task.mLastSurfaceShowing = true; + } + } + } + wc.scheduleAnimation(); + } + @CallSuper void dump(PrintWriter pw, String prefix, boolean dumpAll) { if (mSurfaceAnimator.isAnimating()) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9650b8bc2281..426694d178af 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4087,7 +4087,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Requires READ_FRAME_BUFFER permission"); } - final Bitmap bm; + ScreenCapture.LayerCaptureArgs captureArgs; synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY); if (displayContent == null) { @@ -4095,12 +4095,30 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG_WM, "Screenshot returning null. No Display for displayId=" + DEFAULT_DISPLAY); } - bm = null; + captureArgs = null; } else { - bm = displayContent.screenshotDisplayLocked(); + captureArgs = displayContent.getLayerCaptureArgs(); } } + final Bitmap bm; + if (captureArgs != null) { + ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture = + ScreenCapture.createSyncCaptureListener(); + + ScreenCapture.captureLayers(captureArgs, syncScreenCapture); + + final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = + syncScreenCapture.getBuffer(); + bm = screenshotBuffer == null ? null : screenshotBuffer.asBitmap(); + } else { + bm = null; + } + + if (bm == null) { + Slog.w(TAG_WM, "Failed to take screenshot"); + } + FgThread.getHandler().post(() -> { try { receiver.onHandleAssistScreenshot(bm); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 4ba52e4c0fd7..3f889c01bafb 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -1471,7 +1471,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final int index = task.mChildren.indexOf(topTaskFragment); task.mChildren.remove(taskFragment); task.mChildren.add(index, taskFragment); - effects |= TRANSACT_EFFECTS_LIFECYCLE; + if (taskFragment.hasChild()) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } else { + // Ensure that the child layers are updated if the TaskFragment is empty + task.assignChildLayers(); + } } } break; @@ -1486,7 +1491,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (task != null) { task.mChildren.remove(taskFragment); task.mChildren.add(0, taskFragment); - effects |= TRANSACT_EFFECTS_LIFECYCLE; + if (taskFragment.hasChild()) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } else { + // Ensure that the child layers are updated if the TaskFragment is empty. + task.assignChildLayers(); + } } break; } @@ -1495,7 +1505,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (task != null) { task.mChildren.remove(taskFragment); task.mChildren.add(taskFragment); - effects |= TRANSACT_EFFECTS_LIFECYCLE; + if (taskFragment.hasChild()) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } else { + // Ensure that the child layers are updated if the TaskFragment is empty. + task.assignChildLayers(); + } } break; } diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 7b084132ed1c..4403bce484ad 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -571,8 +571,8 @@ static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIE } static jboolean com_android_server_am_CachedAppOptimizer_isFreezerProfileValid(JNIEnv* env) { - int uid = getuid(); - int pid = getpid(); + uid_t uid = getuid(); + pid_t pid = getpid(); return isProfileValidForProcess("Frozen", uid, pid) && isProfileValidForProcess("Unfrozen", uid, pid); diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java index db985fd16749..b1349ea92a4f 100644 --- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java @@ -23,8 +23,8 @@ import android.credentials.ClearCredentialStateException; import android.credentials.ClearCredentialStateRequest; import android.credentials.CredentialProviderInfo; import android.credentials.IClearCredentialStateCallback; -import android.credentials.ui.ProviderData; -import android.credentials.ui.RequestInfo; +import android.credentials.selection.ProviderData; +import android.credentials.selection.RequestInfo; import android.os.CancellationSignal; import android.os.RemoteException; import android.service.credentials.CallingAppInfo; diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index b24accbe3231..3dcf42d80d60 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -27,8 +27,8 @@ import android.credentials.CreateCredentialResponse; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.credentials.ICreateCredentialCallback; -import android.credentials.ui.ProviderData; -import android.credentials.ui.RequestInfo; +import android.credentials.selection.ProviderData; +import android.credentials.selection.RequestInfo; import android.os.CancellationSignal; import android.os.RemoteException; import android.service.credentials.CallingAppInfo; diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 667e086c8b40..281fb1c4635b 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -27,6 +27,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -201,7 +202,7 @@ public final class CredentialManagerService @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same // this.mLock protected void handlePackageRemovedMultiModeLocked(String packageName, int userId) { - updateProvidersWhenPackageRemoved(mContext, packageName); + updateProvidersWhenPackageRemoved(new SettingsWrapper(mContext), packageName); List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId); if (services == null) { @@ -1134,13 +1135,14 @@ public final class CredentialManagerService } /** Updates the list of providers when an app is uninstalled. */ - public static void updateProvidersWhenPackageRemoved(Context context, String packageName) { + public static void updateProvidersWhenPackageRemoved( + SettingsWrapper settingsWrapper, String packageName) { + Slog.i(TAG, "updateProvidersWhenPackageRemoved"); + // Get the current providers. String rawProviders = - Settings.Secure.getStringForUser( - context.getContentResolver(), - Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, - UserHandle.myUserId()); + settingsWrapper.getStringForUser( + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, UserHandle.myUserId()); if (rawProviders == null) { Slog.w(TAG, "settings key is null"); return; @@ -1148,44 +1150,44 @@ public final class CredentialManagerService // Remove any providers from the primary setting that contain the package name // being removed. - Set<String> primaryProviders = - getStoredProviders(rawProviders, packageName); - if (!Settings.Secure.putString( - context.getContentResolver(), + Set<String> primaryProviders = getStoredProviders(rawProviders, packageName); + if (!settingsWrapper.putStringForUser( Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, - String.join(":", primaryProviders))) { - Slog.w(TAG, "Failed to remove primary package: " + packageName); + String.join(":", primaryProviders), + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove primary package: " + packageName); return; } // Read the autofill provider so we don't accidentally erase it. String autofillProvider = - Settings.Secure.getStringForUser( - context.getContentResolver(), - Settings.Secure.AUTOFILL_SERVICE, - UserHandle.myUserId()); + settingsWrapper.getStringForUser( + Settings.Secure.AUTOFILL_SERVICE, UserHandle.myUserId()); // If there is an autofill provider and it is the placeholder indicating // that the currently selected primary provider does not support autofill // then we should wipe the setting to keep it in sync. if (autofillProvider != null && primaryProviders.isEmpty()) { if (autofillProvider.equals(AUTOFILL_PLACEHOLDER_VALUE)) { - if (!Settings.Secure.putString( - context.getContentResolver(), + if (!settingsWrapper.putStringForUser( Settings.Secure.AUTOFILL_SERVICE, - "")) { - Slog.w(TAG, "Failed to remove autofill package: " + packageName); + "", + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove autofill package: " + packageName); } } else { // If the existing autofill provider is from the app being removed // then erase the autofill service setting. ComponentName cn = ComponentName.unflattenFromString(autofillProvider); if (cn != null && cn.getPackageName().equals(packageName)) { - if (!Settings.Secure.putString( - context.getContentResolver(), + if (!settingsWrapper.putStringForUser( Settings.Secure.AUTOFILL_SERVICE, - "")) { - Slog.w(TAG, "Failed to remove autofill package: " + packageName); + "", + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove autofill package: " + packageName); } } } @@ -1193,19 +1195,17 @@ public final class CredentialManagerService // Read the credential providers to remove any reference of the removed app. String rawCredentialProviders = - Settings.Secure.getStringForUser( - context.getContentResolver(), - Settings.Secure.CREDENTIAL_SERVICE, - UserHandle.myUserId()); + settingsWrapper.getStringForUser( + Settings.Secure.CREDENTIAL_SERVICE, UserHandle.myUserId()); // Remove any providers that belong to the removed app. - Set<String> credentialProviders = - getStoredProviders(rawCredentialProviders, packageName); - if (!Settings.Secure.putString( - context.getContentResolver(), + Set<String> credentialProviders = getStoredProviders(rawCredentialProviders, packageName); + if (!settingsWrapper.putStringForUser( Settings.Secure.CREDENTIAL_SERVICE, - String.join(":", credentialProviders))) { - Slog.w(TAG, "Failed to remove secondary package: " + packageName); + String.join(":", credentialProviders), + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove secondary package: " + packageName); } } @@ -1232,4 +1232,38 @@ public final class CredentialManagerService return providers; } + + /** A wrapper class that can be used by tests for intercepting reads/writes. */ + public static class SettingsWrapper { + private final Context mContext; + + public SettingsWrapper(@NonNull Context context) { + this.mContext = context; + } + + ContentResolver getContentResolver() { + return mContext.getContentResolver(); + } + + /** Retrieves the string value of a system setting */ + public String getStringForUser(String name, int userHandle) { + return Settings.Secure.getStringForUser(getContentResolver(), name, userHandle); + } + + /** Updates the string value of a system setting */ + public boolean putStringForUser( + String name, + String value, + int userHandle, + boolean overrideableByRestore) { + return Settings.Secure.putStringForUser( + getContentResolver(), + name, + value, + null, + false, + userHandle, + overrideableByRestore); + } + } } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java index f092dccbcfd1..4203576ac521 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java @@ -22,11 +22,11 @@ import android.content.Context; import android.content.Intent; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; -import android.credentials.ui.DisabledProviderData; -import android.credentials.ui.IntentFactory; -import android.credentials.ui.ProviderData; -import android.credentials.ui.RequestInfo; -import android.credentials.ui.UserSelectionDialogResult; +import android.credentials.selection.DisabledProviderData; +import android.credentials.selection.IntentFactory; +import android.credentials.selection.ProviderData; +import android.credentials.selection.RequestInfo; +import android.credentials.selection.UserSelectionDialogResult; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java index 25281ba89960..7e709fec128b 100644 --- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java @@ -26,9 +26,9 @@ import android.credentials.GetCandidateCredentialsResponse; import android.credentials.GetCredentialRequest; import android.credentials.GetCredentialResponse; import android.credentials.IGetCandidateCredentialsCallback; -import android.credentials.ui.GetCredentialProviderData; -import android.credentials.ui.ProviderData; -import android.credentials.ui.RequestInfo; +import android.credentials.selection.GetCredentialProviderData; +import android.credentials.selection.ProviderData; +import android.credentials.selection.RequestInfo; import android.os.CancellationSignal; import android.os.IBinder; import android.os.RemoteException; diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index 49ea19a6f098..b33f531afdac 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -26,8 +26,8 @@ import android.credentials.GetCredentialException; import android.credentials.GetCredentialRequest; import android.credentials.GetCredentialResponse; import android.credentials.IGetCredentialCallback; -import android.credentials.ui.ProviderData; -import android.credentials.ui.RequestInfo; +import android.credentials.selection.ProviderData; +import android.credentials.selection.RequestInfo; import android.os.Binder; import android.os.CancellationSignal; import android.os.RemoteException; diff --git a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java index efb394d29826..21ac9e44de65 100644 --- a/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java +++ b/services/credentials/java/com/android/server/credentials/PendingIntentResultHandler.java @@ -22,7 +22,7 @@ import android.credentials.CreateCredentialException; import android.credentials.CreateCredentialResponse; import android.credentials.GetCredentialException; import android.credentials.GetCredentialResponse; -import android.credentials.ui.ProviderPendingIntentResponse; +import android.credentials.selection.ProviderPendingIntentResponse; import android.service.credentials.BeginGetCredentialResponse; import android.service.credentials.CredentialProviderService; diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java index fbfc9caf0205..30af56749e05 100644 --- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java @@ -26,9 +26,9 @@ import android.credentials.GetCredentialRequest; import android.credentials.IGetCredentialCallback; import android.credentials.IPrepareGetCredentialCallback; import android.credentials.PrepareGetCredentialResponseInternal; -import android.credentials.ui.GetCredentialProviderData; -import android.credentials.ui.ProviderData; -import android.credentials.ui.RequestInfo; +import android.credentials.selection.GetCredentialProviderData; +import android.credentials.selection.ProviderData; +import android.credentials.selection.RequestInfo; import android.os.CancellationSignal; import android.os.RemoteException; import android.service.credentials.CallingAppInfo; diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java index d4b88001111e..6a1b1db756b5 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java @@ -21,8 +21,8 @@ import android.annotation.UserIdInt; import android.content.Context; import android.credentials.ClearCredentialStateException; import android.credentials.CredentialProviderInfo; -import android.credentials.ui.ProviderData; -import android.credentials.ui.ProviderPendingIntentResponse; +import android.credentials.selection.ProviderData; +import android.credentials.selection.ProviderPendingIntentResponse; import android.os.ICancellationSignal; import android.service.credentials.CallingAppInfo; import android.service.credentials.ClearCredentialStateRequest; diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 6f79852df02f..6361aeb4f0d7 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -25,9 +25,9 @@ import android.content.Intent; import android.credentials.CreateCredentialException; import android.credentials.CreateCredentialResponse; import android.credentials.CredentialProviderInfo; -import android.credentials.ui.CreateCredentialProviderData; -import android.credentials.ui.Entry; -import android.credentials.ui.ProviderPendingIntentResponse; +import android.credentials.selection.CreateCredentialProviderData; +import android.credentials.selection.Entry; +import android.credentials.selection.ProviderPendingIntentResponse; import android.os.Bundle; import android.os.ICancellationSignal; import android.service.credentials.BeginCreateCredentialRequest; diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 7bd1cc4ca6c8..fcaef9ffb11b 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -26,10 +26,10 @@ import android.credentials.CredentialOption; import android.credentials.CredentialProviderInfo; import android.credentials.GetCredentialException; import android.credentials.GetCredentialResponse; -import android.credentials.ui.AuthenticationEntry; -import android.credentials.ui.Entry; -import android.credentials.ui.GetCredentialProviderData; -import android.credentials.ui.ProviderPendingIntentResponse; +import android.credentials.selection.AuthenticationEntry; +import android.credentials.selection.Entry; +import android.credentials.selection.GetCredentialProviderData; +import android.credentials.selection.ProviderPendingIntentResponse; import android.os.ICancellationSignal; import android.service.autofill.Flags; import android.service.credentials.Action; diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java index bafa4a56b28a..f162916690fd 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java @@ -25,10 +25,10 @@ import android.content.Intent; import android.credentials.CredentialOption; import android.credentials.GetCredentialException; import android.credentials.GetCredentialResponse; -import android.credentials.ui.Entry; -import android.credentials.ui.GetCredentialProviderData; -import android.credentials.ui.ProviderData; -import android.credentials.ui.ProviderPendingIntentResponse; +import android.credentials.selection.Entry; +import android.credentials.selection.GetCredentialProviderData; +import android.credentials.selection.ProviderData; +import android.credentials.selection.ProviderPendingIntentResponse; import android.os.ICancellationSignal; import android.service.credentials.CallingAppInfo; import android.service.credentials.CredentialEntry; diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index f2055d0595be..c16e2327abfb 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -25,8 +25,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.credentials.Credential; import android.credentials.CredentialProviderInfo; -import android.credentials.ui.ProviderData; -import android.credentials.ui.ProviderPendingIntentResponse; +import android.credentials.selection.ProviderData; +import android.credentials.selection.ProviderPendingIntentResponse; import android.os.ICancellationSignal; import android.os.RemoteException; import android.util.Slog; diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index 67c52e6e4719..bf7df86890de 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -24,8 +24,8 @@ import android.content.Context; import android.content.Intent; import android.credentials.CredentialProviderInfo; import android.credentials.flags.Flags; -import android.credentials.ui.ProviderData; -import android.credentials.ui.UserSelectionDialogResult; +import android.credentials.selection.ProviderData; +import android.credentials.selection.UserSelectionDialogResult; import android.os.Binder; import android.os.CancellationSignal; import android.os.Handler; diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java index d82834928999..23db11f3c9d1 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java +++ b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java @@ -16,10 +16,10 @@ package com.android.server.credentials.metrics; -import static android.credentials.ui.RequestInfo.TYPE_CREATE; -import static android.credentials.ui.RequestInfo.TYPE_GET; -import static android.credentials.ui.RequestInfo.TYPE_GET_VIA_REGISTRY; -import static android.credentials.ui.RequestInfo.TYPE_UNDEFINED; +import static android.credentials.selection.RequestInfo.TYPE_CREATE; +import static android.credentials.selection.RequestInfo.TYPE_GET; +import static android.credentials.selection.RequestInfo.TYPE_GET_VIA_REGISTRY; +import static android.credentials.selection.RequestInfo.TYPE_UNDEFINED; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_CLEAR_CREDENTIAL; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_CREATE_CREDENTIAL; @@ -32,7 +32,7 @@ import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INI import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNREGISTER_CREDENTIAL_DESCRIPTION; -import android.credentials.ui.RequestInfo; +import android.credentials.selection.RequestInfo; import android.util.Slog; import java.util.AbstractMap; diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java index 83b57c4f78c2..8adcfbcb974c 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -33,7 +33,7 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.credentials.CreateCredentialRequest; import android.credentials.GetCredentialRequest; -import android.credentials.ui.UserSelectionDialogResult; +import android.credentials.selection.UserSelectionDialogResult; import android.util.Slog; import com.android.server.credentials.MetricUtilities; diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java index 4c487a70390d..ba7297790dc9 100644 --- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java +++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java @@ -367,8 +367,11 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider, // TODO(b/312397262): consider virtual displays cases synchronized (mLock) { if (mIsDualDisplayBlockingEnabled - && !mExternalDisplaysConnected.get(displayId, false) - && mDisplayManager.getDisplay(displayId).getType() == TYPE_EXTERNAL) { + && !mExternalDisplaysConnected.get(displayId, false)) { + var display = mDisplayManager.getDisplay(displayId); + if (display == null || display.getType() != TYPE_EXTERNAL) { + return; + } mExternalDisplaysConnected.put(displayId, true); // Only update the supported state when going from 0 external display to 1 diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java index ddf4a089e76e..04cebab107ee 100644 --- a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java +++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java @@ -591,6 +591,20 @@ public final class FoldableDeviceStateProviderTest { } @Test + public void testOnDisplayAddedWithNullDisplayDoesNotThrowNPE() { + createProvider( + createConfig( + /* identifier= */ 1, /* name= */ "ONE", + /* flags= */0, (c) -> true, + FoldableDeviceStateProvider::hasNoConnectedExternalDisplay) + ); + + when(mDisplayManager.getDisplay(1)).thenReturn(null); + // This call should not throw NPE. + mProvider.onDisplayAdded(1); + } + + @Test public void hasNoConnectedDisplay_afterExternalDisplayAddedAndRemoved_returnsTrue() { createProvider( createConfig( diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt index edacda03f277..15c9b9f7a13d 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt @@ -20,7 +20,6 @@ import android.Manifest import android.os.Build import android.util.Slog import com.android.server.permission.access.MutateStateScope -import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports import com.android.server.permission.access.util.andInv import com.android.server.permission.access.util.hasAnyBit @@ -61,10 +60,11 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) { if (version <= 12 /*&& SdkLevel.isAtLeastT()*/) { Slog.v( LOG_TAG, - "Upgrading scoped permissions for package: $packageName" + + "Upgrading scoped media and body sensor permissions for package: $packageName" + ", version: $version, user: $userId" ) upgradeAuralVisualMediaPermissions(packageState, userId) + upgradeBodySensorPermissions(packageState, userId) } // TODO Enable isAtLeastU check, when moving subsystem to mainline. if (version <= 14 /*&& SdkLevel.isAtLeastU()*/) { @@ -182,6 +182,50 @@ class AppIdPermissionUpgrade(private val policy: AppIdPermissionPolicy) { } } + private fun MutateStateScope.upgradeBodySensorPermissions( + packageState: PackageState, + userId: Int + ) { + if ( + Manifest.permission.BODY_SENSORS_BACKGROUND !in + packageState.androidPackage!!.requestedPermissions + ) { + return + } + + // Should have been granted when first getting exempt as if the perm was just split + val appId = packageState.appId + val backgroundBodySensorsFlags = + with(policy) { + getPermissionFlags(appId, userId, Manifest.permission.BODY_SENSORS_BACKGROUND) + } + if (backgroundBodySensorsFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)) { + return + } + + // Add Upgrade Exemption - BODY_SENSORS_BACKGROUND is a restricted permission + with(policy) { + updatePermissionFlags( + appId, + userId, + Manifest.permission.BODY_SENSORS_BACKGROUND, + PermissionFlags.UPGRADE_EXEMPT, + PermissionFlags.UPGRADE_EXEMPT, + ) + } + + val bodySensorsFlags = + with(policy) { getPermissionFlags(appId, userId, Manifest.permission.BODY_SENSORS) } + val isForegroundBodySensorsGranted = PermissionFlags.isAppOpGranted(bodySensorsFlags) + if (isForegroundBodySensorsGranted) { + grantRuntimePermission( + packageState, + userId, + Manifest.permission.BODY_SENSORS_BACKGROUND + ) + } + } + /** Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL] */ private fun MutateStateScope.upgradeUserSelectedVisualMediaPermission( packageState: PackageState, diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java index b9f1ea06aebe..dc9631a8f2e2 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java @@ -116,7 +116,7 @@ public final class ClientControllerTest { ANY_CALLER_PID); verify(invoker.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0)); - assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); + assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); } } @@ -133,7 +133,7 @@ public final class ClientControllerTest { var invoker = IInputMethodClientInvoker.create(mClient, mHandler); added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID, ANY_CALLER_PID); - assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); + assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); assertThat(mController.removeClient(mClient)).isTrue(); } diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp index 3aca1cafbf75..f8accc309931 100644 --- a/services/tests/PackageManagerServiceTests/server/Android.bp +++ b/services/tests/PackageManagerServiceTests/server/Android.bp @@ -103,6 +103,7 @@ android_test { ":PackageParserTestApp4", ":PackageParserTestApp5", ":PackageParserTestApp6", + ":PackageParserTestApp7", ], resource_zips: [":PackageManagerServiceServerTests_apks_as_resources"], 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 71f5c754f22f..a0e0e1ef36ee 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 @@ -15,6 +15,17 @@ */ package com.android.server.pm; +import static android.content.UriRelativeFilter.PATH; +import static android.content.UriRelativeFilter.QUERY; +import static android.content.UriRelativeFilter.FRAGMENT; +import static android.content.UriRelativeFilterGroup.ACTION_ALLOW; +import static android.content.UriRelativeFilterGroup.ACTION_BLOCK; +import static android.os.PatternMatcher.PATTERN_ADVANCED_GLOB; +import static android.os.PatternMatcher.PATTERN_LITERAL; +import static android.os.PatternMatcher.PATTERN_PREFIX; +import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB; +import static android.os.PatternMatcher.PATTERN_SUFFIX; + import static com.android.internal.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS; import static com.google.common.truth.Truth.assertThat; @@ -36,11 +47,15 @@ import static java.util.stream.Collectors.toList; import android.annotation.NonNull; import android.content.Context; +import android.content.IntentFilter; +import android.content.UriRelativeFilter; +import android.content.UriRelativeFilterGroup; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; +import android.content.pm.Flags; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.Property; import android.content.pm.ServiceInfo; @@ -50,6 +65,9 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.ArraySet; import androidx.annotation.Nullable; @@ -106,6 +124,7 @@ import java.lang.reflect.Field; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -123,6 +142,9 @@ public class PackageParserTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private File mTmpDir; private static final File FRAMEWORK = new File("/system/framework/framework-res.apk"); private static final String TEST_APP1_APK = "PackageParserTestApp1.apk"; @@ -131,6 +153,7 @@ public class PackageParserTest { private static final String TEST_APP4_APK = "PackageParserTestApp4.apk"; private static final String TEST_APP5_APK = "PackageParserTestApp5.apk"; private static final String TEST_APP6_APK = "PackageParserTestApp6.apk"; + private static final String TEST_APP7_APK = "PackageParserTestApp7.apk"; private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp"; @Before @@ -375,6 +398,87 @@ public class PackageParserTest { assertNotEquals("$automotive", actualDisplayCategory); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) + public void testParseUriRelativeFilterGroups() throws Exception { + final File testFile = extractFile(TEST_APP7_APK); + try { + final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false); + final List<ParsedActivity> activities = pkg.getActivities(); + final List<ParsedIntentInfo> intents = activities.get(0).getIntents(); + final IntentFilter intentFilter = intents.get(0).getIntentFilter(); + assertEquals(7, intentFilter.countUriRelativeFilterGroups()); + + UriRelativeFilterGroup group = intentFilter.getUriRelativeFilterGroup(0); + Collection<UriRelativeFilter> filters = group.getUriRelativeFilters(); + assertEquals(ACTION_BLOCK, group.getAction()); + assertEquals(3, filters.size()); + assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_PREFIX, "/gizmos"))); + assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_SIMPLE_GLOB, + ".*query=string.*"))); + assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_LITERAL, + "fragment"))); + + group = intentFilter.getUriRelativeFilterGroup(1); + filters = group.getUriRelativeFilters(); + assertEquals(ACTION_ALLOW, group.getAction()); + assertEquals(2, filters.size()); + assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_LITERAL, + "query=string"))); + assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_SUFFIX, + "fragment"))); + + group = intentFilter.getUriRelativeFilterGroup(2); + filters = group.getUriRelativeFilters(); + assertEquals(ACTION_ALLOW, group.getAction()); + assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_LITERAL, "/gizmos"))); + assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_LITERAL, + ".*query=string.*"))); + assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_LITERAL, + "fragment"))); + + group = intentFilter.getUriRelativeFilterGroup(3); + filters = group.getUriRelativeFilters(); + assertEquals(ACTION_ALLOW, group.getAction()); + assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_PREFIX, "/gizmos"))); + assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_PREFIX, + ".*query=string.*"))); + assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_PREFIX, + "fragment"))); + + group = intentFilter.getUriRelativeFilterGroup(4); + filters = group.getUriRelativeFilters(); + assertEquals(ACTION_ALLOW, group.getAction()); + assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_SIMPLE_GLOB, + "/gizmos"))); + assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_SIMPLE_GLOB, + ".*query=string.*"))); + assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_SIMPLE_GLOB, + "fragment"))); + + group = intentFilter.getUriRelativeFilterGroup(5); + filters = group.getUriRelativeFilters(); + assertEquals(ACTION_ALLOW, group.getAction()); + assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_ADVANCED_GLOB, + "/gizmos"))); + assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_ADVANCED_GLOB, + ".*query=string.*"))); + assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_ADVANCED_GLOB, + "fragment"))); + + group = intentFilter.getUriRelativeFilterGroup(6); + filters = group.getUriRelativeFilters(); + assertEquals(ACTION_ALLOW, group.getAction()); + assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_SUFFIX, "/gizmos"))); + assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_SUFFIX, + ".*query=string.*"))); + assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_SUFFIX, + "fragment"))); + } finally { + testFile.delete(); + } + } + private static final int PROPERTY_TYPE_BOOLEAN = 1; private static final int PROPERTY_TYPE_FLOAT = 2; private static final int PROPERTY_TYPE_INTEGER = 3; diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 02e3ef4d5f0b..75febd902dcf 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -61,6 +61,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityOptions.LaunchCookie; import android.app.PropertyInvalidatedCache; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceManager; @@ -1557,7 +1558,7 @@ public class DisplayManagerServiceTest { when(mMockProjectionService .setContentRecordingSession(any(ContentRecordingSession.class), eq(projection))) .thenReturn(true); - doReturn(mock(IBinder.class)).when(projection).getLaunchCookie(); + doReturn(new LaunchCookie()).when(projection).getLaunchCookie(); doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection)); final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index c556aca1313a..64076e604414 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -1727,6 +1727,7 @@ public class DisplayModeDirectorTest { var desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID_2); assertThat(desiredSpecs.primary.render.max).isEqualTo(expectedMaxRenderFrameRate); + assertThat(desiredSpecs.appRequest.render.max).isEqualTo(expectedMaxRenderFrameRate); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java new file mode 100644 index 000000000000..2f12a3b858d2 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; +import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY; +import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_HOME; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; +import static android.app.ActivityManager.PROCESS_STATE_SERVICE; +import static android.content.Context.BIND_AUTO_CREATE; +import static android.content.Context.BIND_INCLUDE_CAPABILITIES; +import static android.content.Context.BIND_WAIVE_PRIORITY; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED; +import static android.os.UserHandle.USER_SYSTEM; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; + +import static com.android.server.am.ProcessList.HOME_APP_ADJ; +import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ; +import static com.android.server.am.ProcessList.SERVICE_ADJ; +import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ; + +import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.IApplicationThread; +import android.app.IServiceConnection; +import android.app.usage.UsageStatsManagerInternal; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManagerInternal; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.DropBoxManagerInternal; +import com.android.server.LocalServices; +import com.android.server.am.ActivityManagerService.Injector; +import com.android.server.am.ApplicationExitInfoTest.ServiceThreadRule; +import com.android.server.appop.AppOpsService; +import com.android.server.firewall.IntentFirewall; +import com.android.server.wm.ActivityTaskManagerService; +import com.android.server.wm.WindowProcessController; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.verification.VerificationMode; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.function.Consumer; + +/** + * Test class for the service timeout. + * + * Build/Install/Run: + * atest ServiceBindingOomAdjPolicyTest + */ +@Presubmit +public final class ServiceBindingOomAdjPolicyTest { + private static final String TAG = ServiceBindingOomAdjPolicyTest.class.getSimpleName(); + + private static final String TEST_APP1_NAME = "com.example.foo"; + private static final String TEST_SERVICE1_NAME = "com.example.foo.Foobar"; + private static final int TEST_APP1_UID = 10123; + private static final int TEST_APP1_PID = 12345; + + private static final String TEST_APP2_NAME = "com.example.bar"; + private static final String TEST_SERVICE2_NAME = "com.example.bar.Buz"; + private static final int TEST_APP2_UID = 10124; + private static final int TEST_APP2_PID = 12346; + + @Rule + public final ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + + private Context mContext; + private HandlerThread mHandlerThread; + + @Mock + private AppOpsService mAppOpsService; + @Mock + private DropBoxManagerInternal mDropBoxManagerInt; + @Mock + private PackageManagerInternal mPackageManagerInt; + @Mock + private UsageStatsManagerInternal mUsageStatsManagerInt; + @Mock + private AppErrors mAppErrors; + @Mock + private IntentFirewall mIntentFirewall; + + private ActivityManagerService mAms; + private ProcessList mProcessList; + private ActiveServices mActiveServices; + + private int mCurrentCallingUid; + private int mCurrentCallingPid; + + /** Run at the test class initialization */ + @BeforeClass + public static void setUpOnce() { + System.setProperty("dexmaker.share_classloader", "true"); + } + + @SuppressWarnings("GuardedBy") + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + final ProcessList realProcessList = new ProcessList(); + mProcessList = spy(realProcessList); + + LocalServices.removeServiceForTest(DropBoxManagerInternal.class); + LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); + doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); + + final ActivityManagerService realAms = new ActivityManagerService( + new TestInjector(mContext), mServiceThreadRule.getThread()); + final ActivityTaskManagerService realAtm = new ActivityTaskManagerService(mContext); + realAtm.initialize(null, null, mContext.getMainLooper()); + realAms.mActivityTaskManager = spy(realAtm); + realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal()); + realAms.mOomAdjuster = spy(realAms.mOomAdjuster); + realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer); + realAms.mPackageManagerInt = mPackageManagerInt; + realAms.mUsageStatsService = mUsageStatsManagerInt; + realAms.mAppProfiler = spy(realAms.mAppProfiler); + realAms.mProcessesReady = true; + mAms = spy(realAms); + realProcessList.mService = mAms; + + doReturn(false).when(mPackageManagerInt).filterAppAccess(anyString(), anyInt(), anyInt()); + doReturn(true).when(mIntentFirewall).checkService(any(), any(), anyInt(), anyInt(), any(), + any()); + doReturn(false).when(mAms.mAtmInternal).hasSystemAlertWindowPermission(anyInt(), anyInt(), + any()); + doReturn(true).when(mAms.mOomAdjuster.mCachedAppOptimizer).useFreezer(); + doNothing().when(mAms.mOomAdjuster.mCachedAppOptimizer).freezeAppAsyncInternalLSP( + any(), anyLong(), anyBoolean()); + doReturn(false).when(mAms.mAppProfiler).updateLowMemStateLSP(anyInt(), anyInt(), + anyInt(), anyLong()); + + mCurrentCallingUid = TEST_APP1_UID; + mCurrentCallingPid = TEST_APP1_PID; + } + + @After + public void tearDown() throws Exception { + LocalServices.removeServiceForTest(DropBoxManagerInternal.class); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + mHandlerThread.quit(); + } + + @Test + public void testServiceSelfBindingOomAdj() throws Exception { + // Enable the flags. + mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be 0 oom adj updates. + performTestServiceSelfBindingOomAdj(never(), never()); + + // Disable the flags. + mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update. + performTestServiceSelfBindingOomAdj(atLeastOnce(), atLeastOnce()); + } + + @SuppressWarnings("GuardedBy") + private void performTestServiceSelfBindingOomAdj(VerificationMode bindMode, + VerificationMode unbindMode) throws Exception { + final ProcessRecord app = addProcessRecord( + TEST_APP1_PID, // pid + TEST_APP1_UID, // uid + PROCESS_STATE_SERVICE, // procstate + SERVICE_ADJ, // adj + PROCESS_CAPABILITY_NONE, // capabilities + TEST_APP1_NAME // packageName + ); + final Intent serviceIntent = createServiceIntent(TEST_APP1_NAME, TEST_SERVICE1_NAME, + TEST_APP1_UID); + final IServiceConnection serviceConnection = mock(IServiceConnection.class); + + // Make a self binding. + assertNotEquals(0, mAms.bindService( + app.getThread(), // caller + null, // token + serviceIntent, // service + null, // resolveType + serviceConnection, // connection + BIND_AUTO_CREATE, // flags + TEST_APP1_NAME, // callingPackage + USER_SYSTEM // userId + )); + + verify(mAms.mOomAdjuster, bindMode).updateOomAdjPendingTargetsLocked(anyInt()); + clearInvocations(mAms.mOomAdjuster); + + // Unbind the service. + mAms.unbindService(serviceConnection); + + verify(mAms.mOomAdjuster, unbindMode).updateOomAdjPendingTargetsLocked(anyInt()); + clearInvocations(mAms.mOomAdjuster); + + removeProcessRecord(app); + } + + @Test + public void testServiceDistinctBindingOomAdjMoreImportant() throws Exception { + // Enable the flags. + mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + // because the client is more important. + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + PROCESS_CAPABILITY_NONE, TEST_APP1_NAME, + this::setHasForegroundServices, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, + HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHomeProcess, + BIND_AUTO_CREATE, + atLeastOnce(), atLeastOnce()); + + // Disable the flags. + mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + // because the client is more important. + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + PROCESS_CAPABILITY_NONE, TEST_APP1_NAME, + this::setHasForegroundServices, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, + HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHomeProcess, + BIND_AUTO_CREATE, + atLeastOnce(), atLeastOnce()); + } + + @Test + public void testServiceDistinctBindingOomAdjLessImportant() throws Exception { + // Enable the flags. + mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be 0 oom adj update + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE, + never(), never()); + + // Disable the flags. + mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE, + atLeastOnce(), atLeastOnce()); + } + + @Test + public void testServiceDistinctBindingOomAdjWaivePriority() throws Exception { + // Enable the flags. + mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be 0 oom adj update for binding + // because we're using the BIND_WAIVE_PRIORITY; + // but for the unbinding, because client is better than service, we can't skip it safely. + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + PROCESS_CAPABILITY_NONE, TEST_APP1_NAME, + this::setHasForegroundServices, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, + HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHomeProcess, + BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY, + never(), atLeastOnce()); + + // Verify that there should be 0 oom adj update + // because we're using the BIND_WAIVE_PRIORITY; + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY, + never(), never()); + + // Disable the flags. + mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + // because the client is more important. + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + PROCESS_CAPABILITY_NONE, TEST_APP1_NAME, + this::setHasForegroundServices, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, + HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHomeProcess, + BIND_AUTO_CREATE, + atLeastOnce(), atLeastOnce()); + } + + @Test + public void testServiceDistinctBindingOomAdjNoIncludeCapabilities() throws Exception { + // Enable the flags. + mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be 0 oom adj update + // because we didn't specify the "BIND_INCLUDE_CAPABILITIES" + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, + PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE, + never(), never()); + + // Disable the flags. + mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, + PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE, + atLeastOnce(), atLeastOnce()); + } + + @Test + public void testServiceDistinctBindingOomAdjWithIncludeCapabilities() throws Exception { + // Enable the flags. + mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + // because we use the "BIND_INCLUDE_CAPABILITIES" + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, + PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE | BIND_INCLUDE_CAPABILITIES, + atLeastOnce(), atLeastOnce()); + + // Disable the flags. + mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, + PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE | BIND_INCLUDE_CAPABILITIES, + atLeastOnce(), atLeastOnce()); + } + + @Test + public void testServiceDistinctBindingOomAdjFreezeCaller() throws Exception { + // Enable the flags. + mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be 0 oom adj update + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE, + TEST_APP1_NAME, null, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE, + never(), never()); + + // Disable the flags. + mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + + // Verify that there should be at least 1 oom adj update + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE, + TEST_APP1_NAME, null, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE, + atLeastOnce(), atLeastOnce()); + } + + @SuppressWarnings("GuardedBy") + private void performTestServiceDistinctBindingOomAdj(int clientPid, int clientUid, + int clientProcState, int clientAdj, int clientCap, String clientPackageName, + Consumer<ProcessRecord> clientAppFixer, + int servicePid, int serviceUid, int serviceProcState, int serviceAdj, + int serviceCap, String servicePackageName, String serviceName, + Consumer<ProcessRecord> serviceAppFixer, int bindingFlags, + VerificationMode bindMode, VerificationMode unbindMode) throws Exception { + final ProcessRecord clientApp = addProcessRecord( + clientPid, + clientUid, + clientProcState, + clientAdj, + clientCap, + clientPackageName + ); + final ProcessRecord serviceApp = addProcessRecord( + servicePid, + serviceUid, + serviceProcState, + serviceAdj, + serviceCap, + servicePackageName + ); + final Intent serviceIntent = createServiceIntent(servicePackageName, serviceName, + serviceUid); + final IServiceConnection serviceConnection = mock(IServiceConnection.class); + if (clientAppFixer != null) clientAppFixer.accept(clientApp); + if (serviceAppFixer != null) serviceAppFixer.accept(serviceApp); + + // Make a self binding. + assertNotEquals(0, mAms.bindService( + clientApp.getThread(), // caller + null, // token + serviceIntent, // service + null, // resolveType + serviceConnection, // connection + bindingFlags, // flags + clientPackageName, // callingPackage + USER_SYSTEM // userId + )); + + verify(mAms.mOomAdjuster, bindMode).updateOomAdjPendingTargetsLocked(anyInt()); + clearInvocations(mAms.mOomAdjuster); + + if (clientApp.isFreezable()) { + verify(mAms.mOomAdjuster.mCachedAppOptimizer, + times(Flags.serviceBindingOomAdjPolicy() ? 1 : 0)) + .freezeAppAsyncInternalLSP(eq(clientApp), eq(0L), anyBoolean()); + clearInvocations(mAms.mOomAdjuster.mCachedAppOptimizer); + } + + // Unbind the service. + mAms.unbindService(serviceConnection); + + verify(mAms.mOomAdjuster, unbindMode).updateOomAdjPendingTargetsLocked(anyInt()); + clearInvocations(mAms.mOomAdjuster); + + removeProcessRecord(clientApp); + removeProcessRecord(serviceApp); + } + + private void setHasForegroundServices(ProcessRecord app) { + app.mServices.setHasForegroundServices(true, + FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED, false); + } + + private void setHomeProcess(ProcessRecord app) { + final WindowProcessController wpc = app.getWindowProcessController(); + doReturn(true).when(wpc).isHomeProcess(); + } + + @SuppressWarnings("GuardedBy") + private ProcessRecord addProcessRecord(int pid, int uid, int procState, int adj, int cap, + String packageName) { + final IApplicationThread appThread = mock(IApplicationThread.class); + final IBinder threadBinder = mock(IBinder.class); + final ProcessRecord app = makeProcessRecord(pid, uid, uid, null, 0, + procState, adj, cap, 0L, 0L, packageName, packageName, mAms); + + app.makeActive(appThread, mAms.mProcessStats); + doReturn(threadBinder).when(appThread).asBinder(); + mProcessList.addProcessNameLocked(app); + mProcessList.updateLruProcessLocked(app, false, null); + + setFieldValue(ProcessRecord.class, app, "mWindowProcessController", + mock(WindowProcessController.class)); + + doReturn(app.getSetCapability()).when(mAms.mOomAdjuster).getDefaultCapability( + eq(app), anyInt()); + + return app; + } + + @SuppressWarnings("GuardedBy") + private Intent createServiceIntent(String packageName, String serviceName, int serviceUid) { + final ComponentName compName = new ComponentName(packageName, serviceName); + final Intent serviceIntent = new Intent().setComponent(compName); + final ResolveInfo rInfo = new ResolveInfo(); + rInfo.serviceInfo = makeServiceInfo(compName.getClassName(), compName.getPackageName(), + serviceUid); + doReturn(rInfo).when(mPackageManagerInt).resolveService(any(Intent.class), any(), + anyLong(), anyInt(), anyInt()); + + return serviceIntent; + } + + @SuppressWarnings("GuardedBy") + private void removeProcessRecord(ProcessRecord app) { + app.setKilled(true); + mProcessList.removeProcessNameLocked(app.processName, app.uid); + mProcessList.removeLruProcessLocked(app); + } + + @SuppressWarnings("GuardedBy") + private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, + int connectionGroup, int procState, int adj, int cap, long pss, long rss, + String processName, String packageName, ActivityManagerService ams) { + final ProcessRecord app = ApplicationExitInfoTest.makeProcessRecord(pid, uid, packageUid, + definingUid, connectionGroup, procState, pss, rss, processName, packageName, ams); + app.mState.setCurProcState(procState); + app.mState.setSetProcState(procState); + app.mState.setCurAdj(adj); + app.mState.setSetAdj(adj); + app.mState.setCurCapability(cap); + app.mState.setSetCapability(cap); + app.mState.setCached(procState >= PROCESS_STATE_LAST_ACTIVITY || adj >= CACHED_APP_MIN_ADJ); + return app; + } + + @SuppressWarnings("GuardedBy") + private ServiceInfo makeServiceInfo(String serviceName, String packageName, int packageUid) { + final ServiceInfo sInfo = new ServiceInfo(); + sInfo.name = serviceName; + sInfo.processName = packageName; + sInfo.packageName = packageName; + sInfo.applicationInfo = new ApplicationInfo(); + sInfo.applicationInfo.uid = packageUid; + sInfo.applicationInfo.packageName = packageName; + sInfo.exported = true; + return sInfo; + } + + private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + Field mfield = Field.class.getDeclaredField("accessFlags"); + mfield.setAccessible(true); + mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE)); + field.set(obj, val); + } catch (NoSuchFieldException | IllegalAccessException e) { + } + } + + private class TestInjector extends Injector { + TestInjector(Context context) { + super(context); + } + + @Override + public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile, + Handler handler) { + return mAppOpsService; + } + + @Override + public Handler getUiHandler(ActivityManagerService service) { + return mHandlerThread.getThreadHandler(); + } + + @Override + public ProcessList getProcessList(ActivityManagerService service) { + return mProcessList; + } + + @Override + public ActiveServices getActiveServices(ActivityManagerService service) { + if (mActiveServices == null) { + mActiveServices = spy(new ActiveServices(service)); + } + return mActiveServices; + } + + @Override + public int getCallingUid() { + return mCurrentCallingUid; + } + + @Override + public int getCallingPid() { + return mCurrentCallingPid; + } + + @Override + public long clearCallingIdentity() { + return (((long) mCurrentCallingUid) << 32) | mCurrentCallingPid; + } + + @Override + public void restoreCallingIdentity(long ident) { + } + + @Override + public AppErrors getAppErrors() { + return mAppErrors; + } + + @Override + public IntentFirewall getIntentFirewall() { + return mIntentFirewall; + } + } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java index 8d9a6c510576..9a143d5b3743 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java @@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import android.content.Context; +import android.frameworks.location.altitude.GetGeoidHeightRequest; +import android.frameworks.location.altitude.GetGeoidHeightResponse; import android.location.Location; import android.location.altitude.AltitudeConverter; @@ -176,4 +178,20 @@ public class AltitudeConverterTest { assertThrows(IllegalArgumentException.class, () -> mAltitudeConverter.addMslAltitudeToLocation(mContext, location)); } + + @Test + public void testGetGeoidHeight_expectedBehavior() throws IOException { + GetGeoidHeightRequest request = new GetGeoidHeightRequest(); + request.latitudeDegrees = -35.334815; + request.longitudeDegrees = -45; + // Requires data to be loaded from raw assets. + GetGeoidHeightResponse response = mAltitudeConverter.getGeoidHeight(mContext, request); + assertThat(response.geoidHeightMeters).isWithin(2).of(-5.0622); + assertThat(response.geoidHeightErrorMeters).isGreaterThan(0f); + assertThat(response.geoidHeightErrorMeters).isLessThan(1f); + assertThat(response.expirationDistanceMeters).isWithin(1).of(-6.33); + assertThat(response.additionalGeoidHeightErrorMeters).isGreaterThan(0f); + assertThat(response.additionalGeoidHeightErrorMeters).isLessThan(1f); + assertThat(response.success).isTrue(); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java index a65ef00f8a21..bf00b75e9f7b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java @@ -552,20 +552,22 @@ public class PackageArchiverTest { when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn( null); - assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); + assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT, + CALLER_PACKAGE)).isNull(); } @Test public void getArchivedAppIcon_notArchived() { - assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); + assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT, + CALLER_PACKAGE)).isNull(); } @Test public void getArchivedAppIcon_success() { mUserState.setArchiveState(createArchiveState()).setInstalled(false); - assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isEqualTo( - mIcon); + assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT, + CALLER_PACKAGE)).isEqualTo(mIcon); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index bb70080362b1..92513760fa4a 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -21,6 +21,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; @@ -96,18 +99,11 @@ public class BatteryStatsHistoryTest { mClock.realtime = 123; mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024, - mStepDetailsCalculator, mClock, mMonotonicClock, mTracer) { - @Override - public boolean readFileToParcel(Parcel out, AtomicFile file) { - mReadFiles.add(file.getBaseFile().getName()); - return super.readFileToParcel(out, file); - } - }; + mStepDetailsCalculator, mClock, mMonotonicClock, mTracer); when(mStepDetailsCalculator.getHistoryStepDetails()) .thenReturn(new BatteryStats.HistoryStepDetails()); - mHistoryPrinter = new BatteryStats.HistoryPrinter(); } @@ -276,6 +272,15 @@ public class BatteryStatsHistoryTest { mReadFiles.clear(); + // Make an immutable copy and spy on it + mHistory = spy(mHistory.copy()); + + doAnswer(invocation -> { + AtomicFile file = invocation.getArgument(1); + mReadFiles.add(file.getBaseFile().getName()); + return invocation.callRealMethod(); + }).when(mHistory).readFileToParcel(any(), any()); + // Prepare history for iteration mHistory.iterate(0, MonotonicClock.UNDEFINED); @@ -309,6 +314,15 @@ public class BatteryStatsHistoryTest { mReadFiles.clear(); + // Make an immutable copy and spy on it + mHistory = spy(mHistory.copy()); + + doAnswer(invocation -> { + AtomicFile file = invocation.getArgument(1); + mReadFiles.add(file.getBaseFile().getName()); + return invocation.callRealMethod(); + }).when(mHistory).readFileToParcel(any(), any()); + // Prepare history for iteration mHistory.iterate(1000, 3000); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java index 4dae2d548057..8e53d5285cc4 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java @@ -28,6 +28,9 @@ import static org.mockito.Mockito.when; import android.os.BatteryConsumer; import android.os.Binder; import android.os.Process; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -38,6 +41,7 @@ import com.android.internal.os.KernelCpuUidTimeReader; import com.android.internal.os.KernelSingleUidTimeReader; import com.android.internal.os.PowerProfile; import com.android.internal.power.EnergyConsumerStats; +import com.android.server.power.optimization.Flags; import org.junit.Before; import org.junit.Rule; @@ -54,6 +58,8 @@ import java.util.Collection; @RunWith(AndroidJUnit4.class) @SuppressWarnings("GuardedBy") public class SystemServicePowerCalculatorTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); private static final double PRECISION = 0.000001; private static final int APP_UID1 = 100; @@ -108,6 +114,7 @@ public class SystemServicePowerCalculatorTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR) public void testPowerProfileBasedModel() { prepareBatteryStats(null); @@ -135,6 +142,7 @@ public class SystemServicePowerCalculatorTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR) public void testMeasuredEnergyBasedModel() { final boolean[] supportedPowerBuckets = new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS]; diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 8958fac87bb6..e22d99d45521 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -36,6 +36,7 @@ android_test { "-Werror", ], static_libs: [ + "cts-input-lib", "frameworks-base-testutils", "services.accessibility", "services.appwidget", diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt new file mode 100644 index 000000000000..52c7d8d2bd2e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt @@ -0,0 +1,280 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.accessibility + +import android.hardware.display.DisplayManagerGlobal +import android.os.SystemClock +import android.view.Display +import android.view.Display.DEFAULT_DISPLAY +import android.view.DisplayAdjustments +import android.view.DisplayInfo +import android.view.IInputFilterHost +import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.InputEvent +import android.view.MotionEvent +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.ACTION_UP +import android.view.MotionEvent.ACTION_HOVER_ENTER +import android.view.MotionEvent.ACTION_HOVER_EXIT +import android.view.MotionEvent.ACTION_HOVER_MOVE +import android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.cts.input.inputeventmatchers.withDeviceId +import com.android.cts.input.inputeventmatchers.withMotionAction +import com.android.server.LocalServices +import com.android.server.accessibility.magnification.MagnificationProcessor +import com.android.server.wm.WindowManagerInternal +import java.util.concurrent.LinkedBlockingQueue +import org.hamcrest.Matchers.allOf +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.stubbing.OngoingStubbing + + +/** + * Create a MotionEvent with the provided action, eventTime, and source + */ +fun createMotionEvent(action: Int, downTime: Long, eventTime: Long, source: Int, deviceId: Int): + MotionEvent { + val x = 1f + val y = 2f + val pressure = 3f + val size = 1f + val metaState = 0 + val xPrecision = 0f + val yPrecision = 0f + val edgeFlags = 0 + val displayId = 0 + return MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, size, metaState, + xPrecision, yPrecision, deviceId, edgeFlags, source, displayId) +} + +/** + * Tests for AccessibilityInputFilter, focusing on the input event processing as seen by the callers + * of the InputFilter interface. + * The main interaction with AccessibilityInputFilter in these tests is with the filterInputEvent + * and sendInputEvent APIs of InputFilter. + */ +@RunWith(AndroidJUnit4::class) +class AccessibilityInputFilterInputTest { + private val instrumentation = InstrumentationRegistry.getInstrumentation() + + private companion object{ + const val ALL_A11Y_FEATURES = (AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK + or AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION + or AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER + or AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER + or AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS + or AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS) + } + + @Rule + @JvmField + val mocks: MockitoRule = MockitoJUnit.rule() + + @Mock + private lateinit var mockA11yController: WindowManagerInternal.AccessibilityControllerInternal + + @Mock + private lateinit var mockWindowManagerService: WindowManagerInternal + + @Mock + private lateinit var mockMagnificationProcessor: MagnificationProcessor + + private val inputEvents = LinkedBlockingQueue<InputEvent>() + private val verifier = BlockingQueueEventVerifier(inputEvents) + + @Mock + private lateinit var host: IInputFilterHost + private lateinit var ams: AccessibilityManagerService + private lateinit var a11yInputFilter: AccessibilityInputFilter + private val touchDeviceId = 1 + + @Before + fun setUp() { + val context = instrumentation.context + LocalServices.removeServiceForTest(WindowManagerInternal::class.java) + LocalServices.addService(WindowManagerInternal::class.java, mockWindowManagerService) + + whenever(mockA11yController.isAccessibilityTracingEnabled).thenReturn(false) + whenever( + mockWindowManagerService.accessibilityController).thenReturn( + mockA11yController) + + ams = Mockito.spy(AccessibilityManagerService(context)) + val displayList = arrayListOf(createStubDisplay(DEFAULT_DISPLAY, DisplayInfo())) + whenever(ams.validDisplayList).thenReturn(displayList) + whenever(ams.magnificationProcessor).thenReturn(mockMagnificationProcessor) + + doAnswer { + val event = it.getArgument(0) as MotionEvent + inputEvents.add(MotionEvent.obtain(event)) + }.`when`(host).sendInputEvent(any(), anyInt()) + + a11yInputFilter = AccessibilityInputFilter(context, ams) + a11yInputFilter.install(host) + } + + @After + fun tearDown() { + if (this::a11yInputFilter.isInitialized) { + a11yInputFilter.uninstall() + } + } + + /** + * When no features are enabled, the events pass through the filter without getting modified. + */ + @Test + fun testSingleDeviceTouchEventsWithoutA11yFeatures() { + enableFeatures(0) + + val downTime = SystemClock.uptimeMillis() + val downEvent = createMotionEvent( + ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId) + send(downEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_DOWN), withDeviceId(touchDeviceId))) + + val moveEvent = createMotionEvent( + ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(moveEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId))) + + val upEvent = createMotionEvent( + ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(upEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId))) + + verifier.assertNoEvents() + } + + /** + * Enable all a11y features and send a touchscreen stream of DOWN -> MOVE -> UP events. + * These get converted into HOVER_ENTER -> HOVER_MOVE -> HOVER_EXIT events by the input filter. + */ + @Test + fun testSingleDeviceTouchEventsWithAllA11yFeatures() { + enableFeatures(ALL_A11Y_FEATURES) + + val downTime = SystemClock.uptimeMillis() + val downEvent = createMotionEvent( + ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId) + send(MotionEvent.obtain(downEvent)) + + // DOWN event gets transformed to HOVER_ENTER + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId))) + + // MOVE becomes HOVER_MOVE + val moveEvent = createMotionEvent( + ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(moveEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_MOVE), withDeviceId(touchDeviceId))) + + // UP becomes HOVER_EXIT + val upEvent = createMotionEvent( + ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(upEvent) + + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId))) + + verifier.assertNoEvents() + } + + /** + * Enable all a11y features and send a touchscreen event stream. In the middle of the gesture, + * disable the a11y features. + * When the a11y features are disabled, the filter generates HOVER_EXIT without further input + * from the dispatcher. + */ + @Test + fun testSingleDeviceTouchEventsDisableFeaturesMidGesture() { + enableFeatures(ALL_A11Y_FEATURES) + + val downTime = SystemClock.uptimeMillis() + val downEvent = createMotionEvent( + ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId) + send(MotionEvent.obtain(downEvent)) + + // DOWN event gets transformed to HOVER_ENTER + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId))) + verifier.assertNoEvents() + + enableFeatures(0) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId))) + verifier.assertNoEvents() + + val moveEvent = createMotionEvent( + ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(moveEvent) + val upEvent = createMotionEvent( + ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(upEvent) + // As the original gesture continues, no additional events should be getting sent by the + // filter because the HOVER_EXIT above already effectively finished the current gesture and + // the DOWN event was never sent to the host. + + // Bug: the down event was swallowed, so the remainder of the gesture should be swallowed + // too. However, the MOVE and UP events are currently passed back to the dispatcher. + // TODO(b/310014874) - ensure a11y sends consistent input streams to the dispatcher + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId))) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId))) + + verifier.assertNoEvents() + } + + private fun createStubDisplay(displayId: Int, displayInfo: DisplayInfo): Display { + val display = Display(DisplayManagerGlobal.getInstance(), displayId, + displayInfo, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS) + return display + } + + private fun send(event: InputEvent) { + // We need to make a copy of the event before sending it to the filter, because the filter + // will recycle it, but the caller of this function might want to still be able to use + // this event for subsequent checks + val eventCopy = if (event is MotionEvent) MotionEvent.obtain(event) else event + a11yInputFilter.filterInputEvent(eventCopy, FLAG_PASS_TO_USER) + } + + private fun enableFeatures(features: Int) { + instrumentation.runOnMainSync { a11yInputFilter.setUserAndEnabledFeatures(0, features) } + } +} + +private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall) diff --git a/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt b/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt new file mode 100644 index 000000000000..b12f537d1482 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.accessibility + +import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS +import android.view.InputEvent +import android.view.MotionEvent +import java.time.Duration +import java.util.concurrent.BlockingQueue +import java.util.concurrent.TimeUnit +import org.junit.Assert.fail + +import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert.assertThat +import org.junit.Assert.assertNull + +private fun <T> getEvent(queue: BlockingQueue<T>, timeout: Duration): T? { + return queue.poll(timeout.toMillis(), TimeUnit.MILLISECONDS) +} + +class BlockingQueueEventVerifier(val queue: BlockingQueue<InputEvent>) { + fun assertReceivedMotion(matcher: Matcher<MotionEvent>) { + val event = getMotionEvent() + assertThat("MotionEvent checks", event, matcher) + } + + fun assertNoEvents() { + val event = getEvent(queue, Duration.ofMillis(50)) + assertNull(event) + } + + private fun getMotionEvent(): MotionEvent { + val event = getEvent(queue, Duration.ofMillis(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong())) + if (event == null) { + fail("Did not get an event") + } + if (event is MotionEvent) { + return event + } + fail("Instead of motion, got $event") + throw RuntimeException("should not reach here") + } +} + diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java index 0815fe52e262..dd5d826b1739 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java @@ -33,6 +33,9 @@ import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -40,6 +43,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; @@ -66,6 +70,9 @@ public class UserAwareBiometricSchedulerTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); private Handler mHandler; private UserAwareBiometricScheduler mScheduler; @@ -122,6 +129,7 @@ public class UserAwareBiometricSchedulerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void testScheduleOperation_whenNoUser() { mCurrentUserId = UserHandle.USER_NULL; @@ -183,6 +191,7 @@ public class UserAwareBiometricSchedulerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void testScheduleOperation_whenSameUser() { mCurrentUserId = 10; @@ -199,6 +208,7 @@ public class UserAwareBiometricSchedulerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void testScheduleOperation_whenDifferentUser() { mCurrentUserId = 10; @@ -219,6 +229,7 @@ public class UserAwareBiometricSchedulerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void testStartUser_alwaysStartsNextOperation() { BaseClientMonitor nextClient = mock(BaseClientMonitor.class); when(nextClient.getTargetUserId()).thenReturn(10); @@ -246,6 +257,7 @@ public class UserAwareBiometricSchedulerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void testStartUser_failsClearsStopUserClient() { // When a stop user client fails, check that mStopUserClient // is set to null to prevent the scheduler from getting stuck. diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java index f8b5b04294cd..84c36848adbf 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -50,12 +50,17 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.TestableContext; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.OperationContextExt; @@ -94,6 +99,9 @@ public class FaceAuthenticationClientTest { @Rule public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private ISession mHal; @@ -127,6 +135,8 @@ public class FaceAuthenticationClientTest { private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; + @Captor + private ArgumentCaptor<Consumer<OperationContext>> mStartHalConsumerCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -149,6 +159,7 @@ public class FaceAuthenticationClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void authWithContext_v2() throws RemoteException { final FaceAuthenticationClient client = createClient(2); client.start(mCallback); @@ -189,6 +200,7 @@ public class FaceAuthenticationClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void notifyHalWhenContextChanges() throws RemoteException { final FaceAuthenticationClient client = createClient(); client.start(mCallback); @@ -211,6 +223,36 @@ public class FaceAuthenticationClientTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void subscribeContextAndStartHal() throws RemoteException { + final FaceAuthenticationClient client = createClient(); + client.start(mCallback); + + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + + mStartHalConsumerCaptor.getValue().accept( + mOperationContextCaptor.getValue().toAidlContext()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + + verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture()); + + OperationContext opContext = captor.getValue(); + + assertThat(opContext).isSameInstanceAs( + mOperationContextCaptor.getValue().toAidlContext()); + + mContextInjector.getValue().accept(opContext); + + verify(mHal).onContextChanged(same(opContext)); + + client.stopHalOperation(); + + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); + } + + @Test public void cancelsAuthWhenNotInForeground() throws Exception { final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo(); topTask.topActivity = new ComponentName("other", "thing"); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java index fbf0e13c2ac9..e626f7307e7e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java @@ -37,11 +37,16 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.Vibrator; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.TestableContext; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.OperationContextExt; @@ -71,6 +76,9 @@ public class FaceDetectClientTest { @Rule public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private ISession mHal; @@ -91,6 +99,8 @@ public class FaceDetectClientTest { @Captor private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor + private ArgumentCaptor<Consumer<OperationContext>> mStartHalCaptor; + @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; @Rule @@ -114,6 +124,7 @@ public class FaceDetectClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void detectWithContext_v2() throws RemoteException { final FaceDetectClient client = createClient(2); client.start(mCallback); @@ -132,6 +143,7 @@ public class FaceDetectClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void notifyHalWhenContextChanges() throws RemoteException { final FaceDetectClient client = createClient(); client.start(mCallback); @@ -154,6 +166,35 @@ public class FaceDetectClientTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void subscribeContextAndStartHal() throws RemoteException { + final FaceDetectClient client = createClient(); + client.start(mCallback); + + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalCaptor.capture(), mContextInjector.capture(), any()); + + mStartHalCaptor.getValue().accept(mOperationContextCaptor.getValue().toAidlContext()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + + verify(mHal).detectInteractionWithContext(captor.capture()); + + OperationContext opContext = captor.getValue(); + + assertThat(opContext).isSameInstanceAs( + mOperationContextCaptor.getValue().toAidlContext()); + + mContextInjector.getValue().accept(opContext); + + verify(mHal).onContextChanged(same(opContext)); + + client.stopHalOperation(); + + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); + } + + @Test public void doesNotPlayHapticOnInteractionDetected() throws Exception { final FaceDetectClient client = createClient(); client.start(mCallback); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java index d662620407e8..43ed07a0bf76 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java @@ -28,17 +28,21 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.hardware.biometrics.common.OperationContext; -import android.hardware.biometrics.face.FaceEnrollOptions; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.TestableContext; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.OperationContextExt; @@ -68,6 +72,9 @@ public class FaceEnrollClientTest { @Rule public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private ISession mHal; @@ -89,6 +96,8 @@ public class FaceEnrollClientTest { private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; + @Captor + private ArgumentCaptor<Consumer<OperationContext>> mStartHalConsumer; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -109,6 +118,7 @@ public class FaceEnrollClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void enrollWithContext_v2() throws RemoteException { final FaceEnrollClient client = createClient(2); client.start(mCallback); @@ -123,6 +133,7 @@ public class FaceEnrollClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void notifyHalWhenContextChanges() throws RemoteException { final FaceEnrollClient client = createClient(3); client.start(mCallback); @@ -145,6 +156,37 @@ public class FaceEnrollClientTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void subscribeContextAndStartHal() throws RemoteException { + final FaceEnrollClient client = createClient(3); + client.start(mCallback); + + verify(mBiometricContext).subscribe( + mOperationContextCaptor.capture(), mStartHalConsumer.capture(), + mContextInjector.capture(), any()); + + mStartHalConsumer.getValue().accept(mOperationContextCaptor.getValue().toAidlContext()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + + verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), captor.capture()); + + OperationContext opContext = captor.getValue(); + + assertThat(opContext).isSameInstanceAs( + mOperationContextCaptor.getValue().toAidlContext()); + + mContextInjector.getValue().accept(opContext); + + verify(mHal).onContextChanged(same(opContext)); + + client.stopHalOperation(); + + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void enrollWithFaceOptions() throws RemoteException { final FaceEnrollClient client = createClient(4); client.start(mCallback); @@ -152,6 +194,20 @@ public class FaceEnrollClientTest { verify(mHal).enrollWithOptions(any()); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void enrollWithFaceOptionsAfterSubscribingContext() throws RemoteException { + final FaceEnrollClient client = createClient(4); + client.start(mCallback); + + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumer.capture(), any(), any()); + + mStartHalConsumer.getValue().accept(mOperationContextCaptor.getValue().toAidlContext()); + + verify(mHal).enrollWithOptions(any()); + } + private FaceEnrollClient createClient() throws RemoteException { return createClient(200 /* version */); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java index 78c1e08ba832..949d6ee800fa 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java @@ -38,11 +38,15 @@ import android.os.IBinder; import android.os.Looper; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.internal.R; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.AuthenticationStateListeners; import com.android.server.biometrics.sensors.BiometricScheduler; @@ -50,6 +54,7 @@ import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -70,6 +75,10 @@ public class Face10Test { private static final int USER_ID = 20; private static final float FRR_THRESHOLD = 0.2f; + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private Context mContext; @Mock @@ -145,6 +154,7 @@ public class Face10Test { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void scheduleGenerateChallenge_cachesResult() { final IFaceServiceReceiver[] mocks = IntStream.range(0, 3) .mapToObj(i -> mock(IFaceServiceReceiver.class)) @@ -163,6 +173,7 @@ public class Face10Test { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void scheduleRevokeChallenge_waitsUntilEmpty() { final long challenge = 22; final IFaceServiceReceiver[] mocks = IntStream.range(0, 3) @@ -182,6 +193,7 @@ public class Face10Test { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void scheduleRevokeChallenge_doesNotWaitForever() { mFace10.scheduleGenerateChallenge( SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG); 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 4ed6f74d30fa..f96d9e841eba 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 @@ -56,6 +56,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -158,6 +159,9 @@ public class FingerprintAuthenticationClientTest { private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; + @Captor + private ArgumentCaptor<Consumer<OperationContext>> mStartHalConsumerCaptor; + private final TestLooper mLooper = new TestLooper(); @Before @@ -175,12 +179,19 @@ public class FingerprintAuthenticationClientTest { public void authNoContext_v1() throws RemoteException { final FingerprintAuthenticationClient client = createClient(1); client.start(mCallback); + if (Flags.deHidl()) { + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor + .getValue().toAidlContext()); + } verify(mHal).authenticate(eq(OP_ID)); verify(mHal, never()).authenticateWithContext(anyLong(), any()); } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void authWithContext_v2() throws RemoteException { final FingerprintAuthenticationClient client = createClient(2); client.start(mCallback); @@ -262,15 +273,24 @@ public class FingerprintAuthenticationClientTest { public void luxProbeWhenAwake() throws RemoteException { when(mBiometricContext.isAwake()).thenReturn(false); when(mBiometricContext.isAod()).thenReturn(false); + final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); + if (Flags.deHidl()) { + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor + .getValue().toAidlContext()); + } final ArgumentCaptor<OperationContext> captor = ArgumentCaptor.forClass(OperationContext.class); verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture()); OperationContext opContext = captor.getValue(); - verify(mBiometricContext).subscribe( - mOperationContextCaptor.capture(), mContextInjector.capture()); + if (!Flags.deHidl()) { + verify(mBiometricContext).subscribe( + mOperationContextCaptor.capture(), mContextInjector.capture()); + } assertThat(mOperationContextCaptor.getValue().toAidlContext()) .isSameInstanceAs(opContext); @@ -305,6 +325,12 @@ public class FingerprintAuthenticationClientTest { when(mBiometricContext.isAod()).thenReturn(false); final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); + if (Flags.deHidl()) { + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor + .getValue().toAidlContext()); + } verify(mLuxProbe, isAwake ? times(1) : never()).enable(); } @@ -315,13 +341,21 @@ public class FingerprintAuthenticationClientTest { when(mBiometricContext.isAod()).thenReturn(true); final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); + if (Flags.deHidl()) { + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor + .getValue().toAidlContext()); + } final ArgumentCaptor<OperationContext> captor = ArgumentCaptor.forClass(OperationContext.class); verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture()); OperationContext opContext = captor.getValue(); - verify(mBiometricContext).subscribe( - mOperationContextCaptor.capture(), mContextInjector.capture()); + if (!Flags.deHidl()) { + verify(mBiometricContext).subscribe( + mOperationContextCaptor.capture(), mContextInjector.capture()); + } assertThat(opContext).isSameInstanceAs( mOperationContextCaptor.getValue().toAidlContext()); @@ -345,6 +379,7 @@ public class FingerprintAuthenticationClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void notifyHalWhenContextChanges() throws RemoteException { final FingerprintAuthenticationClient client = createClient(); client.start(mCallback); @@ -367,6 +402,36 @@ public class FingerprintAuthenticationClientTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void subscribeContextAndStartHal() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(); + client.start(mCallback); + + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + + mStartHalConsumerCaptor.getValue().accept(mOperationContextCaptor + .getValue().toAidlContext()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + + verify(mHal).authenticateWithContext(eq(OP_ID), captor.capture()); + + OperationContext opContext = captor.getValue(); + + assertThat(opContext).isSameInstanceAs( + mOperationContextCaptor.getValue().toAidlContext()); + + mContextInjector.getValue().accept(opContext); + + verify(mHal).onContextChanged(same(opContext)); + + client.stopHalOperation(); + + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); + } + + @Test public void showHideOverlay_cancel() throws RemoteException { showHideOverlay(c -> c.cancel()); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java index a467c84845cd..9edb8dde182a 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java @@ -35,11 +35,16 @@ import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.TestableContext; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.OperationContextExt; @@ -67,6 +72,9 @@ public class FingerprintDetectClientTest { @Rule public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private ISession mHal; @@ -88,6 +96,8 @@ public class FingerprintDetectClientTest { private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; + @Captor + private ArgumentCaptor<Consumer<OperationContext>> mStartHalConsumerCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -109,6 +119,7 @@ public class FingerprintDetectClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void detectNoContext_v2() throws RemoteException { final FingerprintDetectClient client = createClient(2); @@ -127,6 +138,7 @@ public class FingerprintDetectClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void notifyHalWhenContextChanges() throws RemoteException { final FingerprintDetectClient client = createClient(); client.start(mCallback); @@ -149,6 +161,31 @@ public class FingerprintDetectClientTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void subscribeContextAndStartHal() throws RemoteException { + final FingerprintDetectClient client = createClient(); + client.start(mCallback); + + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + + mStartHalConsumerCaptor.getValue().accept( + mOperationContextCaptor.getValue().toAidlContext()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + verify(mHal).detectInteractionWithContext(captor.capture()); + OperationContext opContext = captor.getValue(); + + assertThat(opContext).isSameInstanceAs( + mOperationContextCaptor.getValue().toAidlContext()); + mContextInjector.getValue().accept(opContext); + verify(mHal).onContextChanged(same(opContext)); + + client.stopHalOperation(); + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); + } + + @Test public void testWhenListenerIsNull() { final AidlSession aidl = new AidlSession(0, mHal, USER_ID, mAidlResponseHandler); final FingerprintDetectClient client = new FingerprintDetectClient(mContext, () -> aidl, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java index e7d4a2e463f7..951c93935e30 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java @@ -40,12 +40,17 @@ import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.TestableContext; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; @@ -73,6 +78,9 @@ import java.util.function.Consumer; public class FingerprintEnrollClientTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); private static final byte[] HAT = new byte[69]; private static final int USER_ID = 8; @@ -117,6 +125,8 @@ public class FingerprintEnrollClientTest { private ArgumentCaptor<OperationContextExt> mOperationContextCaptor; @Captor private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; + @Captor + private ArgumentCaptor<Consumer<OperationContext>> mStartHalConsumerCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -140,6 +150,7 @@ public class FingerprintEnrollClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void enrollWithContext_v2() throws RemoteException { final FingerprintEnrollClient client = createClient(2); @@ -236,6 +247,7 @@ public class FingerprintEnrollClientTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DE_HIDL) public void notifyHalWhenContextChanges() throws RemoteException { final FingerprintEnrollClient client = createClient(); client.start(mCallback); @@ -257,6 +269,30 @@ public class FingerprintEnrollClientTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void subscribeContextAndStartHal() throws RemoteException { + final FingerprintEnrollClient client = createClient(); + client.start(mCallback); + + verify(mBiometricContext).subscribe(mOperationContextCaptor.capture(), + mStartHalConsumerCaptor.capture(), mContextInjector.capture(), any()); + + mStartHalConsumerCaptor.getValue().accept( + mOperationContextCaptor.getValue().toAidlContext()); + final ArgumentCaptor<OperationContext> captor = + ArgumentCaptor.forClass(OperationContext.class); + verify(mHal).enrollWithContext(any(), captor.capture()); + OperationContext opContext = captor.getValue(); + + mContextInjector.getValue().accept( + mOperationContextCaptor.getValue().toAidlContext()); + verify(mHal).onContextChanged(same(opContext)); + + client.stopHalOperation(); + verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue())); + } + + @Test public void showHideOverlay_cancel() throws RemoteException { showHideOverlay(c -> c.cancel()); } 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 d850c73ebc26..57f3cc03980e 100644 --- a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java @@ -22,6 +22,7 @@ import android.content.Context; import android.os.UserHandle; import android.provider.Settings; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -40,10 +41,12 @@ import java.util.Set; public final class CredentialManagerServiceTest { Context mContext = null; + MockSettingsWrapper mSettingsWrapper = null; @Before public void setUp() throws CertificateException { mContext = ApplicationProvider.getApplicationContext(); + mSettingsWrapper = new MockSettingsWrapper(mContext); } @Test @@ -81,7 +84,8 @@ public final class CredentialManagerServiceTest { Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, "com.example.test/com.example.test.TestActivity"); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test"); assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo(""); assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE)) @@ -101,7 +105,8 @@ public final class CredentialManagerServiceTest { setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE, testCredentialValue); setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test3"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test3"); // Since the provider removed was not a primary provider then we should do nothing. assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)) @@ -125,7 +130,8 @@ public final class CredentialManagerServiceTest { Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, "com.example.test/com.example.test.TestActivity"); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test"); assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo(""); assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE)) @@ -144,7 +150,8 @@ public final class CredentialManagerServiceTest { setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE, testCredentialValue); setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test3"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test3"); // Since the provider removed was not a primary provider then we should do nothing. assertCredentialPropertyEquals( @@ -176,12 +183,36 @@ public final class CredentialManagerServiceTest { assertThat(actualValueSet).isEqualTo(newValueSet); } - private void setSettingsKey(String key, String value) { - assertThat(Settings.Secure.putString(mContext.getContentResolver(), key, value)).isTrue(); + private void setSettingsKey(String name, String value) { + assertThat( + mSettingsWrapper.putStringForUser( + name, value, UserHandle.myUserId(), true)) + .isTrue(); } - private String getSettingsKey(String key) { - return Settings.Secure.getStringForUser( - mContext.getContentResolver(), key, UserHandle.myUserId()); + private String getSettingsKey(String name) { + return mSettingsWrapper.getStringForUser(name, UserHandle.myUserId()); + } + + private static final class MockSettingsWrapper + extends CredentialManagerService.SettingsWrapper { + + MockSettingsWrapper(@NonNull Context context) { + super(context); + } + + /** Updates the string value of a system setting */ + @Override + public boolean putStringForUser( + String name, + String value, + int userHandle, + boolean overrideableByRestore) { + // This will ensure that when the settings putStringForUser method is called by + // CredentialManagerService that the overrideableByRestore bit is true. + assertThat(overrideableByRestore).isTrue(); + + return Settings.Secure.putStringForUser(getContentResolver(), name, value, userHandle); + } } } diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java index 6bc0fbf3101c..0f3f27aa2896 100644 --- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java @@ -39,8 +39,8 @@ import android.credentials.Credential; import android.credentials.CredentialOption; import android.credentials.GetCredentialException; import android.credentials.GetCredentialResponse; -import android.credentials.ui.GetCredentialProviderData; -import android.credentials.ui.ProviderPendingIntentResponse; +import android.credentials.selection.GetCredentialProviderData; +import android.credentials.selection.ProviderPendingIntentResponse; import android.net.Uri; import android.os.Bundle; import android.service.credentials.CallingAppInfo; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 62073497b735..1bd6e29a7f21 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -659,6 +659,64 @@ public class HdmiCecLocalDevicePlaybackTest { mTestLooper.dispatchAll(); assertThat(mActiveMediaSessionsPaused).isFalse(); } + @Test + public void handleRoutingInformation_physicalAddressOfSender_Tv_activeSourceChange() { + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + mPowerManager.setInteractive(true); + // Physical address reported in this message is the same as message sender's (TV) physical + // address. + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(Constants.ADDR_TV, 0x0000); + + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)) + .isEqualTo(Constants.HANDLED); + assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse(); + assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress).isEqualTo( + 0x0000); + // Active source's logical address is invalidated. + // See {@link HdmiCecLocalDevicePlayback#handleRoutingChangeAndInformation}. + assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo( + ADDR_INVALID); + assertThat(mPowerManager.isInteractive()).isFalse(); + } + + @Test + public void handleRoutingInformation_physicalAddressOfSender_notTv_noActiveSourceChange() { + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); + mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, + mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); + // Add a device to the network and assert that this device is included in the list of + // devices. + HdmiDeviceInfo infoPlayback = HdmiDeviceInfo.cecDeviceBuilder() + .setLogicalAddress(Constants.ADDR_PLAYBACK_3) + .setPhysicalAddress(0x1000) + .setPortId(PORT_1) + .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK) + .setVendorId(0x1000) + .setDisplayName("Playback 3") + .build(); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback); + mPowerManager.setInteractive(true); + // Physical address reported in this message is the same as message sender's (Playback_3) + // physical address. + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(Constants.ADDR_PLAYBACK_3, 0x1000); + + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)) + .isEqualTo(Constants.HANDLED); + assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress).isEqualTo( + mPlaybackPhysicalAddress); + assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo( + mPlaybackLogicalAddress); + assertThat(mPowerManager.isInteractive()).isTrue(); + } @Test public void handleSetStreamPath() { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 5e380108aeb3..67ae9984266f 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -170,6 +170,11 @@ public class HdmiCecLocalDeviceTvTest { } @Override + boolean isPowerOnOrTransient() { + return true; + } + + @Override void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) { mDeviceEventListeners.add(new DeviceEventListener(device, status)); } @@ -1839,4 +1844,38 @@ public class HdmiCecLocalDeviceTvTest { assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); } + + @Test + public void handleStandby_fromActiveSource_standby() { + mPowerManager.setInteractive(true); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000, + "HdmiCecLocalDeviceTvTest"); + mTestLooper.dispatchAll(); + + HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1, + ADDR_TV); + assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage)) + .isEqualTo(Constants.HANDLED); + mTestLooper.dispatchAll(); + + assertThat(mPowerManager.isInteractive()).isFalse(); + } + + @Test + public void handleStandby_fromNonActiveSource_noStandby() { + mPowerManager.setInteractive(true); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mHdmiControlService.setActiveSource(ADDR_PLAYBACK_2, 0x2000, + "HdmiCecLocalDeviceTvTest"); + mTestLooper.dispatchAll(); + + HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1, + ADDR_TV); + assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage)) + .isEqualTo(Constants.HANDLED); + mTestLooper.dispatchAll(); + + assertThat(mPowerManager.isInteractive()).isTrue(); + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index eca19c8e8c4d..2da2f50447c7 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -506,6 +506,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } @Test + public void testUnlockUserWithTokenWithBadHandleReturnsFalse() { + final long badTokenHandle = 123456789; + final byte[] token = "some-high-entropy-secure-token".getBytes(); + mService.initializeSyntheticPassword(PRIMARY_USER_ID); + assertFalse(mLocalService.unlockUserWithToken(badTokenHandle, token, PRIMARY_USER_ID)); + } + + @Test public void testGetHashFactorPrimaryUser() throws RemoteException { LockscreenCredential password = newPassword("password"); initSpAndSetCredential(PRIMARY_USER_ID, password); diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index 097cc5177a83..abd3abee82fb 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -49,6 +49,7 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions.LaunchCookie; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; @@ -784,7 +785,7 @@ public class MediaProjectionManagerServiceTest { @RecordContent int recordedContent) throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); - projection.setLaunchCookie(mock(IBinder.class)); + projection.setLaunchCookie(new LaunchCookie()); projection.start(mIMediaProjectionCallback); projection.notifyVirtualDisplayCreated(10); // Waiting for user to review consent. @@ -825,7 +826,7 @@ public class MediaProjectionManagerServiceTest { public void testSetUserReviewGrantedConsentResult_displayMirroring_noPriorSession() throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); - projection.setLaunchCookie(mock(IBinder.class)); + projection.setLaunchCookie(new LaunchCookie()); projection.start(mIMediaProjectionCallback); // Skip setting the prior session details. @@ -844,7 +845,7 @@ public class MediaProjectionManagerServiceTest { public void testSetUserReviewGrantedConsentResult_displayMirroring_sessionNotWaiting() throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); - projection.setLaunchCookie(mock(IBinder.class)); + projection.setLaunchCookie(new LaunchCookie()); projection.start(mIMediaProjectionCallback); // Session is not waiting for user's consent. doReturn(true).when(mWindowManagerInternal).setContentRecordingSession( diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 080548520b0c..81df597f3f33 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -157,6 +157,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return mMockDevicePolicyManager; case Context.APP_SEARCH_SERVICE: case Context.ROLE_SERVICE: + case Context.APP_OPS_SERVICE: // RoleManager is final and cannot be mocked, so we only override the inject // accessor methods in ShortcutService. return getTestContext().getSystemService(name); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java index d7ed7c2d6469..8d8dc9cc45b1 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java @@ -23,6 +23,7 @@ import static org.testng.Assert.assertThrows; import android.content.pm.UserProperties; import android.os.Parcel; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.Xml; import androidx.test.filters.MediumTest; @@ -31,6 +32,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,10 +54,13 @@ import java.util.function.Supplier; @RunWith(AndroidJUnit4.class) @MediumTest public class UserManagerServiceUserPropertiesTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); /** Test that UserProperties can properly read the xml information that it writes. */ @Test public void testWriteReadXml() throws Exception { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); final UserProperties defaultProps = new UserProperties.Builder() .setShowInLauncher(21) .setStartWithParent(false) @@ -73,6 +78,7 @@ public class UserManagerServiceUserPropertiesTest { .setDeleteAppWithParent(false) .setAlwaysVisible(false) .setCrossProfileContentSharingStrategy(0) + .setProfileApiVisibility(34) .build(); final UserProperties actualProps = new UserProperties(defaultProps); actualProps.setShowInLauncher(14); @@ -90,6 +96,7 @@ public class UserManagerServiceUserPropertiesTest { actualProps.setDeleteAppWithParent(true); actualProps.setAlwaysVisible(true); actualProps.setCrossProfileContentSharingStrategy(1); + actualProps.setProfileApiVisibility(36); // Write the properties to xml. final ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -114,6 +121,7 @@ public class UserManagerServiceUserPropertiesTest { /** Tests parcelling an object in which all properties are present. */ @Test public void testParcelUnparcel() throws Exception { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); final UserProperties originalProps = new UserProperties.Builder() .setShowInLauncher(2145) .build(); @@ -124,6 +132,7 @@ public class UserManagerServiceUserPropertiesTest { /** Tests copying a UserProperties object varying permissions. */ @Test public void testCopyLacksPermissions() throws Exception { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); final UserProperties defaultProps = new UserProperties.Builder() .setShowInLauncher(2145) .setStartWithParent(true) @@ -134,6 +143,7 @@ public class UserManagerServiceUserPropertiesTest { .setAuthAlwaysRequiredToDisableQuietMode(false) .setAllowStoppingUserWithDelayedLocking(false) .setAlwaysVisible(true) + .setProfileApiVisibility(110) .build(); final UserProperties orig = new UserProperties(defaultProps); orig.setShowInLauncher(2841); @@ -209,6 +219,8 @@ public class UserManagerServiceUserPropertiesTest { copy::isCredentialShareableWithParent, true); assertEqualGetterOrThrows(orig::getCrossProfileContentSharingStrategy, copy::getCrossProfileContentSharingStrategy, true); + assertEqualGetterOrThrows(orig::getProfileApiVisibility, copy::getProfileApiVisibility, + true); } /** @@ -270,5 +282,6 @@ public class UserManagerServiceUserPropertiesTest { assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible()); assertThat(expected.getCrossProfileContentSharingStrategy()) .isEqualTo(actual.getCrossProfileContentSharingStrategy()); + assertThat(expected.getProfileApiVisibility()).isEqualTo(actual.getProfileApiVisibility()); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index 70837061b0bb..1ee604e78d5f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -41,6 +41,7 @@ import android.content.res.XmlResourceParser; import android.os.Bundle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; import androidx.test.InstrumentationRegistry; @@ -50,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -71,9 +73,11 @@ public class UserManagerServiceUserTypeTest { public void setup() { mResources = InstrumentationRegistry.getTargetContext().getResources(); } - + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Test public void testUserTypeBuilder_createUserType() { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); final Bundle restrictions = makeRestrictionsBundle("r1", "r2"); final Bundle systemSettings = makeSettingsBundle("s1", "s2"); final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2"); @@ -97,7 +101,8 @@ public class UserManagerServiceUserTypeTest { .setInheritDevicePolicy(340) .setDeleteAppWithParent(true) .setAlwaysVisible(true) - .setCrossProfileContentSharingStrategy(1); + .setCrossProfileContentSharingStrategy(1) + .setProfileApiVisibility(34); final UserTypeDetails type = new UserTypeDetails.Builder() .setName("a.name") @@ -180,6 +185,7 @@ public class UserManagerServiceUserTypeTest { assertTrue(type.getDefaultUserPropertiesReference().getAlwaysVisible()); assertEquals(1, type.getDefaultUserPropertiesReference() .getCrossProfileContentSharingStrategy()); + assertEquals(34, type.getDefaultUserPropertiesReference().getProfileApiVisibility()); assertEquals(23, type.getBadgeLabel(0)); assertEquals(24, type.getBadgeLabel(1)); @@ -199,6 +205,7 @@ public class UserManagerServiceUserTypeTest { @Test public void testUserTypeBuilder_defaults() { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); UserTypeDetails type = new UserTypeDetails.Builder() .setName("name") // Required (no default allowed) .setBaseType(FLAG_FULL) // Required (no default allowed) @@ -238,6 +245,8 @@ public class UserManagerServiceUserTypeTest { props.getShowInQuietMode()); assertEquals(UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION, props.getCrossProfileContentSharingStrategy()); + assertEquals(UserProperties.PROFILE_API_VISIBILITY_VISIBLE, + props.getProfileApiVisibility()); assertFalse(type.hasBadge()); } @@ -310,6 +319,7 @@ public class UserManagerServiceUserTypeTest { /** Tests {@link UserTypeFactory#customizeBuilders} for a reasonable xml file. */ @Test public void testUserTypeFactoryCustomize_profile() throws Exception { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); final String userTypeAosp1 = "android.test.1"; // Profile user that is not customized final String userTypeAosp2 = "android.test.2"; // Profile user that is customized final String userTypeOem1 = "custom.test.1"; // Custom-defined profile @@ -332,7 +342,8 @@ public class UserManagerServiceUserTypeTest { .setShowInQuietMode(24) .setDeleteAppWithParent(true) .setAlwaysVisible(false) - .setCrossProfileContentSharingStrategy(1); + .setCrossProfileContentSharingStrategy(1) + .setProfileApiVisibility(36); final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); builders.put(userTypeAosp1, new UserTypeDetails.Builder() @@ -383,6 +394,7 @@ public class UserManagerServiceUserTypeTest { assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible()); assertEquals(1, aospType.getDefaultUserPropertiesReference() .getCrossProfileContentSharingStrategy()); + assertEquals(36, aospType.getDefaultUserPropertiesReference().getProfileApiVisibility()); // userTypeAosp2 should be modified. aospType = builders.get(userTypeAosp2).createUserTypeDetails(); @@ -439,6 +451,7 @@ public class UserManagerServiceUserTypeTest { assertTrue(aospType.getDefaultUserPropertiesReference().getAlwaysVisible()); assertEquals(0, aospType.getDefaultUserPropertiesReference() .getCrossProfileContentSharingStrategy()); + assertEquals(36, aospType.getDefaultUserPropertiesReference().getProfileApiVisibility()); // userTypeOem1 should be created. UserTypeDetails.Builder customType = builders.get(userTypeOem1); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 06be456be0db..db561c436269 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -39,6 +39,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Postsubmit; import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; @@ -55,6 +56,7 @@ import com.google.common.collect.Range; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -97,6 +99,8 @@ public final class UserManagerTest { private UserSwitchWaiter mUserSwitchWaiter; private UserRemovalWaiter mUserRemovalWaiter; private int mOriginalCurrentUserId; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Before public void setUp() throws Exception { @@ -168,6 +172,7 @@ public final class UserManagerTest { @Test public void testCloneUser() throws Exception { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); assumeCloneEnabled(); UserHandle mainUser = mUserManager.getMainUser(); assumeTrue("Main user is null", mainUser != null); @@ -224,6 +229,7 @@ public final class UserManagerTest { .isEqualTo(cloneUserProperties.getCrossProfileContentSharingStrategy()); assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent); assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible); + assertThrows(SecurityException.class, cloneUserProperties::getProfileApiVisibility); compareDrawables(mUserManager.getUserBadge(), Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain())); @@ -305,6 +311,7 @@ public final class UserManagerTest { @Test public void testPrivateProfile() throws Exception { + mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES); UserHandle mainUser = mUserManager.getMainUser(); assumeTrue("Main user is null", mainUser != null); // Get the default properties for private profile user type. @@ -346,7 +353,8 @@ public final class UserManagerTest { assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent); assertThrows(SecurityException.class, privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking); - + assertThrows(SecurityException.class, + privateProfileUserProperties::getProfileApiVisibility); compareDrawables(mUserManager.getUserBadge(), Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain())); 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 757abde2041e..e3ee21a450c7 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 @@ -436,6 +436,13 @@ class AndroidPackageParsingValidationTest { validateTagCount("action", 20000, tag) validateTagCount("category", 40000, tag) validateTagCount("data", 40000, tag) + validateTagCount("uri-relative-filter-group", 100, tag) + } + + @Test + fun parseUriRelativeFilterGroupTag() { + val tag = "uri-relative-filter-group" + validateTagCount("data", 100, tag) } @Test @@ -465,6 +472,54 @@ class AndroidPackageParsingValidationTest { R.styleable.AndroidManifestData_pathAdvancedPattern, 4000 ) + validateTagAttr(tag, "query", R.styleable.AndroidManifestData_query, 4000) + validateTagAttr( + tag, + "queryPattern", + R.styleable.AndroidManifestData_queryPattern, + 4000 + ) + validateTagAttr( + tag, + "queryPrefix", + R.styleable.AndroidManifestData_queryPrefix, + 4000 + ) + validateTagAttr(tag, + "querySuffix", + R.styleable.AndroidManifestData_querySuffix, + 4000 + ) + validateTagAttr( + tag, + "queryAdvancedPattern", + R.styleable.AndroidManifestData_queryAdvancedPattern, + 4000 + ) + validateTagAttr(tag, "fragment", R.styleable.AndroidManifestData_query, 4000) + validateTagAttr( + tag, + "fragmentPattern", + R.styleable.AndroidManifestData_fragmentPattern, + 4000 + ) + validateTagAttr( + tag, + "fragmentPrefix", + R.styleable.AndroidManifestData_fragmentPrefix, + 4000 + ) + validateTagAttr(tag, + "fragmentSuffix", + R.styleable.AndroidManifestData_fragmentSuffix, + 4000 + ) + validateTagAttr( + tag, + "fragmentAdvancedPattern", + R.styleable.AndroidManifestData_fragmentAdvancedPattern, + 4000 + ) validateTagAttr(tag, "mimeType", R.styleable.AndroidManifestData_mimeType, 255) validateTagAttr(tag, "mimeGroup", R.styleable.AndroidManifestData_mimeGroup, 1024) } diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp index 3e78f9a9674a..131b380d9215 100644 --- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp +++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp @@ -102,3 +102,17 @@ android_test_helper_app { resource_dirs: ["res"], manifest: "AndroidManifestApp6.xml", } + +android_test_helper_app { + name: "PackageParserTestApp7", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + resource_dirs: ["res"], + manifest: "AndroidManifestApp7.xml", +} diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp7.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp7.xml new file mode 100644 index 000000000000..cb87a48eb524 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp7.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" > + + <application> + <activity android:name=".TestActivity" + android:exported="true" > + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + <data android:scheme="http" + android:host="www.example.com" /> + <uri-relative-filter-group android:allow="false"> + <data android:pathPrefix="/gizmos" /> + <data android:queryPattern=".*query=string.*" /> + <data android:fragment="fragment" /> + </uri-relative-filter-group> + <uri-relative-filter-group> + <data android:query="query=string" /> + <data android:fragmentSuffix="fragment" /> + </uri-relative-filter-group> + <uri-relative-filter-group> + <data android:path="/gizmos" /> + <data android:query=".*query=string.*" /> + <data android:fragment="fragment" /> + </uri-relative-filter-group> + <uri-relative-filter-group> + <data android:pathPrefix="/gizmos" /> + <data android:queryPrefix=".*query=string.*" /> + <data android:fragmentPrefix="fragment" /> + </uri-relative-filter-group> + <uri-relative-filter-group> + <data android:pathPattern="/gizmos" /> + <data android:queryPattern=".*query=string.*" /> + <data android:fragmentPattern="fragment" /> + </uri-relative-filter-group> + <uri-relative-filter-group> + <data android:pathAdvancedPattern="/gizmos" /> + <data android:queryAdvancedPattern=".*query=string.*" /> + <data android:fragmentAdvancedPattern="fragment" /> + </uri-relative-filter-group> + <uri-relative-filter-group> + <data android:pathSuffix="/gizmos" /> + <data android:querySuffix=".*query=string.*" /> + <data android:fragmentSuffix="fragment" /> + </uri-relative-filter-group> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file 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 227265aa0a5b..87e822c4149f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -4762,6 +4762,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setShouldDimWallpaper(true) .setShouldUseNightMode(true) .build(); + user1Rule.zenPolicy = new ZenPolicy(); verifyNoMoreInteractions(mDeviceEffectsApplier); mZenModeHelper.onUserSwitched(1); @@ -5042,6 +5043,109 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void removeAndAddAutomaticZenRule_wasActive_isRestoredAsInactive() { + // Start with a rule. + mZenModeHelper.mConfig.automaticRules.clear(); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setConditionId(CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + + // User customizes it. + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + + // App activates it. + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS); + + // App deletes it. + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + + // App adds it again. + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // The rule is restored... + assertThat(newRuleId).isEqualTo(ruleId); + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); + + // ... but it is NOT active + ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(newRuleId); + assertThat(storedRule.isAutomaticActive()).isFalse(); + assertThat(storedRule.isTrueOrUnknown()).isFalse(); + assertThat(storedRule.condition).isNull(); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void removeAndAddAutomaticZenRule_wasSnoozed_isRestoredAsInactive() { + // Start with a rule. + mZenModeHelper.mConfig.automaticRules.clear(); + AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setConditionId(CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + + // User customizes it. + AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) + .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, UPDATE_ORIGIN_USER, "userUpdate", + Process.SYSTEM_UID); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + + // App activates it. + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS); + + // User snoozes it. + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "snoozing", "systemui", Process.SYSTEM_UID); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + + // App deletes it. + mZenModeHelper.removeAutomaticZenRule(ruleId, UPDATE_ORIGIN_APP, "delete it", + CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0); + assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1); + + // App adds it again. + String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule, + UPDATE_ORIGIN_APP, "add it again", CUSTOM_PKG_UID); + + // The rule is restored... + assertThat(newRuleId).isEqualTo(ruleId); + AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId); + assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); + + // ... but it is NEITHER active NOR snoozed. + ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(newRuleId); + assertThat(storedRule.isAutomaticActive()).isFalse(); + assertThat(storedRule.isTrueOrUnknown()).isFalse(); + assertThat(storedRule.condition).isNull(); + assertThat(storedRule.snoozing).isFalse(); + assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + } + + @Test public void testRuleCleanup() throws Exception { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); Instant now = Instant.ofEpochMilli(1701796461000L); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java index 6e5baee3dc67..37e0818eb083 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java @@ -46,6 +46,7 @@ import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.IRemoteCallback; import android.platform.test.annotations.Presubmit; import android.util.Log; import android.util.Rational; @@ -63,6 +64,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; /** * Build/Install/Run: @@ -119,6 +121,29 @@ public class ActivityOptionsTest { } @Test + public void testAbortListenerCalled() { + AtomicBoolean callbackCalled = new AtomicBoolean(false); + + ActivityOptions options = ActivityOptions.makeBasic(); + options.setOnAnimationAbortListener(new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) { + callbackCalled.set(true); + } + }); + + // Verify that the callback is called on abort + options.abort(); + assertTrue(callbackCalled.get()); + + // Verify that the callback survives saving to bundle + ActivityOptions optionsCopy = ActivityOptions.fromBundle(options.toBundle()); + callbackCalled.set(false); + optionsCopy.abort(); + assertTrue(callbackCalled.get()); + } + + @Test public void testTransferLaunchCookie() { final Binder cookie = new Binder(); final ActivityOptions options = ActivityOptions.makeBasic(); @@ -279,7 +304,9 @@ public class ActivityOptionsTest { case "android.activity.pendingIntentCreatorBackgroundActivityStartMode": // KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE case "android.activity.launchCookie": // KEY_LAUNCH_COOKIE + case "android:activity.animAbortListener": // KEY_ANIM_ABORT_LISTENER // Existing keys + break; default: unknownKeys.add(key); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 9e00f927a568..5d14334aaf69 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -417,6 +417,8 @@ public class DisplayPolicyTests extends WindowTestsBase { di.logicalWidth, di.logicalHeight).mConfigInsets.top); } + // Flush the pending change (DecorInsets.Info#mNeedUpdate) for the rotation to be tested. + displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, di.logicalHeight, di.logicalWidth); // Add a window that provides the same insets in current rotation. But it specifies // different insets in other rotations. final WindowState bar2 = createWindow(null, navbar.mAttrs.type, "bar2"); @@ -446,6 +448,12 @@ public class DisplayPolicyTests extends WindowTestsBase { // The insets in other rotations should be still updated. assertEquals(doubleHeightFor90, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, di.logicalHeight, di.logicalWidth).mConfigInsets.bottom); + // Restore to previous height and the insets can still be updated. + bar2.mAttrs.paramsForRotation[Surface.ROTATION_90].providedInsets[0].setInsetsSize( + Insets.of(0, 0, 0, NAV_BAR_HEIGHT)); + assertFalse(displayPolicy.updateDecorInsetsInfo()); + assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, + di.logicalHeight, di.logicalWidth).mConfigInsets.bottom); navbar.removeIfPossible(); bar2.removeIfPossible(); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 3b4b220907f7..9930c88b1e48 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.ActivityManager.RECENT_WITH_EXCLUDED; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; @@ -1207,6 +1208,29 @@ public class RecentTasksTest extends WindowTestsBase { } @Test + public void addTask_tasksAreAddedAccordingToZOrder() { + final Task firstTask = new TaskBuilder(mSupervisor).setTaskId(1) + .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); + final Task secondTask = new TaskBuilder(mSupervisor).setTaskId(2) + .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); + + assertEquals(-1, firstTask.compareTo(secondTask)); + + // initial addition when tasks are created + mRecentTasks.add(firstTask); + mRecentTasks.add(secondTask); + + assertRecentTasksOrder(secondTask, firstTask); + + // Tasks are added in a different order + mRecentTasks.add(secondTask); + mRecentTasks.add(firstTask); + + // order in recents don't change as first task has lower z-order + assertRecentTasksOrder(secondTask, firstTask); + } + + @Test public void removeTask_callsTaskNotificationController() { final Task task = createTaskBuilder(".Task").build(); 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 245b2c5a7d6b..98ca0947a1cf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.Manifest.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE; +import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; @@ -24,11 +26,14 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -65,12 +70,14 @@ import android.window.TaskFragmentOrganizer; import androidx.test.filters.MediumTest; import com.android.server.pm.pkg.AndroidPackage; +import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.util.Collections; import java.util.Set; @@ -592,6 +599,70 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test + public void testIsAllowedToBeEmbeddedInTrustedMode_withManageActivityTasksPermission() { + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .createActivityCount(1) + .build(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + + // Not allow embedding activity if not a trusted host. + assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST, + taskFragment.isAllowedToEmbedActivity(activity)); + + MockitoSession session = + mockitoSession().spyStatic(ActivityTaskManagerService.class).startMocking(); + try { + doReturn(PERMISSION_GRANTED).when(() -> { + return ActivityTaskManagerService.checkPermission( + eq(MANAGE_ACTIVITY_TASKS), anyInt(), anyInt()); + }); + // With the MANAGE_ACTIVITY_TASKS permission, trusted embedding is always allowed. + assertTrue(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); + } finally { + session.finishMocking(); + } + } + + @Test + public void testIsAllowedToEmbedActivityInUntrustedMode_withUntrustedEmbeddingAnyAppPermission( + ) { + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .createActivityCount(1) + .build(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + + // Not allow embedding activity if not a trusted host. + assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST, + taskFragment.isAllowedToEmbedActivity(activity)); + + MockitoSession session = + mockitoSession() + .spyStatic(ActivityTaskManagerService.class) + .spyStatic(Flags.class) + .startMocking(); + try { + doReturn(PERMISSION_GRANTED).when(() -> { + return ActivityTaskManagerService.checkPermission( + eq(EMBED_ANY_APP_IN_UNTRUSTED_MODE), anyInt(), anyInt()); + }); + // With the EMBED_ANY_APP_IN_UNTRUSTED_MODE permission, untrusted embedding is always + // allowed, but it doesn't always allow trusted embedding. + doReturn(true).when(() -> Flags.untrustedEmbeddingAnyAppPermission()); + assertTrue(taskFragment.isAllowedToEmbedActivityInUntrustedMode(activity)); + assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedMode(activity)); + + // If the flag is disabled, the permission doesn't have effect. + doReturn(false).when(() -> Flags.untrustedEmbeddingAnyAppPermission()); + assertFalse(taskFragment.isAllowedToEmbedActivityInUntrustedMode(activity)); + assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedMode(activity)); + } finally { + session.finishMocking(); + } + } + + @Test public void testIgnoreRequestedOrientationForActivityEmbeddingSplit() { // Setup two activities in ActivityEmbedding split. final Task task = createTask(mDisplayContent); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 07cfbf094e5d..16f963fd6c3e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -628,6 +628,27 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { } @Test + public void testLaunchWindowingModeUpdatesExistingTask() { + final TestDisplayContent freeformDisplay = createNewDisplayContent( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea(); + ActivityRecord activity = createSourceActivity(freeformDisplay); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + + assertEquals(RESULT_CONTINUE, + new CalculateRequestBuilder() + .setTask(task) + .setOptions(options) + .calculate()); + + assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode); + } + + @Test public void testBoundsInOptionsInfersFreeformWithResizeableActivity() { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchBounds(new Rect(0, 0, 100, 100)); diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 63db29713825..b7706a926a3d 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -1283,6 +1283,17 @@ public final class PhoneAccount implements Parcelable { sb.append(mExtras); sb.append(" GroupId: "); sb.append(Log.pii(mGroupId)); + sb.append(" SC Restrictions: "); + if (hasSimultaneousCallingRestriction()) { + sb.append("[ "); + for (PhoneAccountHandle handle : mSimultaneousCallingRestriction) { + sb.append(handle); + sb.append(" "); + } + sb.append("]"); + } else { + sb.append("[NONE]"); + } sb.append("]"); return sb.toString(); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a73c46b12c53..e5a94c302c89 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2179,6 +2179,14 @@ public class CarrierConfigManager { */ public static final String KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT = "mms_network_release_timeout_millis_int"; + /** + * Maximum size in bytes of the PDU to send or download when connected to a non-terrestrial + * network. MmsService will return a result code of MMS_ERROR_TOO_LARGE_FOR_TRANSPORT if + * the PDU exceeds this limit when connected to a non-terrestrial network. + * @hide + */ + public static final String KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT = + "mms_max_ntn_payload_size_bytes_int"; /** * The flatten {@link android.content.ComponentName componentName} of the activity that can @@ -10376,6 +10384,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT, -1); sDefaults.putInt(KEY_MMS_SUBJECT_MAX_LENGTH_INT, 40); sDefaults.putInt(KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT, 5 * 1000); + sDefaults.putInt(KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT, 3 * 1000); sDefaults.putString(KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, ""); sDefaults.putString(KEY_MMS_HTTP_PARAMS_STRING, ""); sDefaults.putString(KEY_MMS_NAI_SUFFIX_STRING, ""); diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index c958aba1d758..b7baabf7c319 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -3123,6 +3123,12 @@ public final class SmsManager { @FlaggedApi(Flags.FLAG_MMS_DISABLED_ERROR) public static final int MMS_ERROR_MMS_DISABLED_BY_CARRIER = 12; + /** + * The MMS pdu was too large to send or too large to download over the current connection. + * @hide + */ + public static final int MMS_ERROR_TOO_LARGE_FOR_TRANSPORT = 13; + /** Intent extra name for MMS sending result data in byte array type */ public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA"; /** Intent extra name for HTTP status code for MMS HTTP failure in integer type */ diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 1bf11df7059a..eb7e67dccfd5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1125,7 +1125,7 @@ public class SubscriptionManager { /** * TelephonyProvider column name for satellite attach enabled for carrier. The value of this * column is set based on user settings. - * By default, it's disabled. + * By default, it's enabled. * <P>Type: INTEGER (int)</P> * @hide */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index c1ceaef64d5d..a0f033860b03 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -18970,11 +18970,11 @@ public class TelephonyManager { @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY) @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi - public void setEnableNullCipherNotifications(boolean enable) { + public void setNullCipherNotificationsEnabled(boolean enable) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - telephony.setEnableNullCipherNotifications(enable); + telephony.setNullCipherNotificationsEnabled(enable); } else { throw new IllegalStateException("telephony service is null."); } diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java index 8e79ca5cf3d3..9aaa986435cd 100644 --- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java @@ -28,7 +28,7 @@ import java.util.function.Consumer; /** * A callback class for listening to satellite datagrams. * {@link SatelliteDatagramCallback} is registered to telephony when an app which invokes - * {@link SatelliteManager#registerForSatelliteDatagram(Executor, SatelliteDatagramCallback)}, + * {@link SatelliteManager#registerForIncomingDatagram(Executor, SatelliteDatagramCallback)}, * and {@link #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)} will be invoked * when a new datagram is received from satellite. * diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index a1ac477d3519..b97822a9d913 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -146,7 +146,7 @@ public final class SatelliteManager { /** * Bundle key to get the response from - * {@link #requestIsSatelliteEnabled(Executor, OutcomeReceiver)}. + * {@link #requestIsEnabled(Executor, OutcomeReceiver)}. * @hide */ @@ -162,7 +162,7 @@ public final class SatelliteManager { /** * Bundle key to get the response from - * {@link #requestIsSatelliteSupported(Executor, OutcomeReceiver)}. + * {@link #requestIsSupported(Executor, OutcomeReceiver)}. * @hide */ @@ -170,7 +170,7 @@ public final class SatelliteManager { /** * Bundle key to get the response from - * {@link #requestSatelliteCapabilities(Executor, OutcomeReceiver)}. + * {@link #requestCapabilities(Executor, OutcomeReceiver)}. * @hide */ @@ -178,7 +178,7 @@ public final class SatelliteManager { /** * Bundle key to get the response from - * {@link #requestIsSatelliteProvisioned(Executor, OutcomeReceiver)}. + * {@link #requestIsProvisioned(Executor, OutcomeReceiver)}. * @hide */ @@ -186,7 +186,7 @@ public final class SatelliteManager { /** * Bundle key to get the response from - * {@link #requestIsSatelliteCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}. + * {@link #requestIsCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}. * @hide */ @@ -493,7 +493,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, + public void requestEnabled(boolean enableSatellite, boolean enableDemoMode, @NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { Objects.requireNonNull(executor); @@ -535,7 +535,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void requestIsSatelliteEnabled(@NonNull @CallbackExecutor Executor executor, + public void requestIsEnabled(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -647,7 +647,7 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. */ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void requestIsSatelliteSupported(@NonNull @CallbackExecutor Executor executor, + public void requestIsSupported(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -701,7 +701,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void requestSatelliteCapabilities(@NonNull @CallbackExecutor Executor executor, + public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -967,7 +967,8 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void startSatelliteTransmissionUpdates(@NonNull @CallbackExecutor Executor executor, + @SuppressWarnings("SamShouldBeLast") + public void startTransmissionUpdates(@NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener, @NonNull SatelliteTransmissionUpdateCallback callback) { Objects.requireNonNull(executor); @@ -1029,7 +1030,7 @@ public final class SatelliteManager { * {@link #SATELLITE_RESULT_SUCCESS}. All other results that this operation failed. * * @param callback The callback that was passed to {@link - * #startSatelliteTransmissionUpdates(Executor, Consumer, SatelliteTransmissionUpdateCallback)}. + * #startTransmissionUpdates(Executor, Consumer, SatelliteTransmissionUpdateCallback)}. * @param executor The executor on which the error code listener will be called. * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * @@ -1037,13 +1038,14 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void stopSatelliteTransmissionUpdates( - @NonNull SatelliteTransmissionUpdateCallback callback, - @NonNull @CallbackExecutor Executor executor, - @SatelliteResult @NonNull Consumer<Integer> resultListener) { + public void stopTransmissionUpdates(@NonNull SatelliteTransmissionUpdateCallback callback, + @SuppressWarnings("ListenerLast") @NonNull @CallbackExecutor Executor executor, + @SuppressWarnings("ListenerLast") @SatelliteResult @NonNull + Consumer<Integer> resultListener) { Objects.requireNonNull(callback); Objects.requireNonNull(executor); Objects.requireNonNull(resultListener); + ISatelliteTransmissionUpdateCallback internalCallback = sSatelliteTransmissionUpdateCallbackMap.remove(callback); @@ -1094,7 +1096,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData, + public void provisionService(@NonNull String token, @NonNull byte[] provisionData, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { @@ -1135,11 +1137,11 @@ public final class SatelliteManager { * {@link SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean)} * should report as deprovisioned. * For provisioning satellite service, refer to - * {@link #provisionSatelliteService(String, byte[], CancellationSignal, Executor, Consumer)} + * {@link #provisionService(String, byte[], CancellationSignal, Executor, Consumer)} * * @param token The token of the device/subscription to be deprovisioned. * This should match with the token passed as input in - * {@link #provisionSatelliteService(String, byte[], CancellationSignal, Executor, + * {@link #provisionService(String, byte[], CancellationSignal, Executor, * Consumer)} * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * @@ -1147,7 +1149,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void deprovisionSatelliteService(@NonNull String token, + public void deprovisionService(@NonNull String token, @NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { Objects.requireNonNull(token); @@ -1188,7 +1190,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @SatelliteResult public int registerForSatelliteProvisionStateChanged( + @SatelliteResult public int registerForProvisionStateChanged( @NonNull @CallbackExecutor Executor executor, @NonNull SatelliteProvisionStateCallback callback) { Objects.requireNonNull(executor); @@ -1224,14 +1226,14 @@ public final class SatelliteManager { * If callback was not registered before, the request will be ignored. * * @param callback The callback that was passed to - * {@link #registerForSatelliteProvisionStateChanged(Executor, SatelliteProvisionStateCallback)} + * {@link #registerForProvisionStateChanged(Executor, SatelliteProvisionStateCallback)} * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void unregisterForSatelliteProvisionStateChanged( + public void unregisterForProvisionStateChanged( @NonNull SatelliteProvisionStateCallback callback) { Objects.requireNonNull(callback); ISatelliteProvisionStateCallback internalCallback = @@ -1243,13 +1245,13 @@ public final class SatelliteManager { if (internalCallback != null) { telephony.unregisterForSatelliteProvisionStateChanged(mSubId, internalCallback); } else { - loge("unregisterForSatelliteProvisionStateChanged: No internal callback."); + loge("unregisterForProvisionStateChanged: No internal callback."); } } else { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - loge("unregisterForSatelliteProvisionStateChanged() RemoteException: " + ex); + loge("unregisterForProvisionStateChanged() RemoteException: " + ex); ex.rethrowAsRuntimeException(); } } @@ -1269,7 +1271,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void requestIsSatelliteProvisioned(@NonNull @CallbackExecutor Executor executor, + public void requestIsProvisioned(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -1322,7 +1324,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @SatelliteResult public int registerForSatelliteModemStateChanged( + @SatelliteResult public int registerForModemStateChanged( @NonNull @CallbackExecutor Executor executor, @NonNull SatelliteModemStateCallback callback) { Objects.requireNonNull(executor); @@ -1356,14 +1358,14 @@ public final class SatelliteManager { * If callback was not registered before, the request will be ignored. * * @param callback The callback that was passed to - * {@link #registerForSatelliteModemStateChanged(Executor, SatelliteModemStateCallback)}. + * {@link #registerForModemStateChanged(Executor, SatelliteModemStateCallback)}. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void unregisterForSatelliteModemStateChanged( + public void unregisterForModemStateChanged( @NonNull SatelliteModemStateCallback callback) { Objects.requireNonNull(callback); ISatelliteModemStateCallback internalCallback = sSatelliteModemStateCallbackMap.remove( @@ -1373,15 +1375,15 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForSatelliteModemStateChanged(mSubId, internalCallback); + telephony.unregisterForModemStateChanged(mSubId, internalCallback); } else { - loge("unregisterForSatelliteModemStateChanged: No internal callback."); + loge("unregisterForModemStateChanged: No internal callback."); } } else { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - loge("unregisterForSatelliteModemStateChanged() RemoteException:" + ex); + loge("unregisterForModemStateChanged() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } } @@ -1390,7 +1392,7 @@ public final class SatelliteManager { * Register to receive incoming datagrams over satellite. * * To poll for pending satellite datagrams, refer to - * {@link #pollPendingSatelliteDatagrams(Executor, Consumer)} + * {@link #pollPendingDatagrams(Executor, Consumer)} * * @param executor The executor on which the callback will be called. * @param callback The callback to handle incoming datagrams over satellite. @@ -1403,7 +1405,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @SatelliteResult public int registerForSatelliteDatagram( + @SatelliteResult public int registerForIncomingDatagram( @NonNull @CallbackExecutor Executor executor, @NonNull SatelliteDatagramCallback callback) { Objects.requireNonNull(executor); @@ -1436,12 +1438,12 @@ public final class SatelliteManager { } }; sSatelliteDatagramCallbackMap.put(callback, internalCallback); - return telephony.registerForSatelliteDatagram(mSubId, internalCallback); + return telephony.registerForIncomingDatagram(mSubId, internalCallback); } else { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - loge("registerForSatelliteDatagram() RemoteException:" + ex); + loge("registerForIncomingDatagram() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } return SATELLITE_RESULT_REQUEST_FAILED; @@ -1452,14 +1454,14 @@ public final class SatelliteManager { * If callback was not registered before, the request will be ignored. * * @param callback The callback that was passed to - * {@link #registerForSatelliteDatagram(Executor, SatelliteDatagramCallback)}. + * {@link #registerForIncomingDatagram(Executor, SatelliteDatagramCallback)}. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void unregisterForSatelliteDatagram(@NonNull SatelliteDatagramCallback callback) { + public void unregisterForIncomingDatagram(@NonNull SatelliteDatagramCallback callback) { Objects.requireNonNull(callback); ISatelliteDatagramCallback internalCallback = sSatelliteDatagramCallbackMap.remove(callback); @@ -1468,15 +1470,15 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForSatelliteDatagram(mSubId, internalCallback); + telephony.unregisterForIncomingDatagram(mSubId, internalCallback); } else { - loge("unregisterForSatelliteDatagram: No internal callback."); + loge("unregisterForIncomingDatagram: No internal callback."); } } else { throw new IllegalStateException("telephony service is null."); } } catch (RemoteException ex) { - loge("unregisterForSatelliteDatagram() RemoteException:" + ex); + loge("unregisterForIncomingDatagram() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } } @@ -1497,7 +1499,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void pollPendingSatelliteDatagrams(@NonNull @CallbackExecutor Executor executor, + public void pollPendingDatagrams(@NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { Objects.requireNonNull(executor); Objects.requireNonNull(resultListener); @@ -1512,13 +1514,13 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.pollPendingSatelliteDatagrams(mSubId, internalCallback); + telephony.pollPendingDatagrams(mSubId, internalCallback); } else { executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("pollPendingSatelliteDatagrams() RemoteException:" + ex); + loge("pollPendingDatagrams() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } } @@ -1550,7 +1552,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void sendSatelliteDatagram(@DatagramType int datagramType, + public void sendDatagram(@DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { @@ -1568,14 +1570,14 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.sendSatelliteDatagram(mSubId, datagramType, datagram, + telephony.sendDatagram(mSubId, datagramType, datagram, needFullScreenPointingUI, internalCallback); } else { executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("sendSatelliteDatagram() RemoteException:" + ex); + loge("sendDatagram() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } } @@ -1596,7 +1598,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void requestIsSatelliteCommunicationAllowedForCurrentLocation( + public void requestIsCommunicationAllowedForCurrentLocation( @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) { Objects.requireNonNull(executor); @@ -1626,14 +1628,14 @@ public final class SatelliteManager { } } }; - telephony.requestIsSatelliteCommunicationAllowedForCurrentLocation(mSubId, + telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId, receiver); } else { executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); } } catch (RemoteException ex) { - loge("requestIsSatelliteCommunicationAllowedForCurrentLocation() RemoteException: " + loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: " + ex); ex.rethrowAsRuntimeException(); } @@ -1733,7 +1735,7 @@ public final class SatelliteManager { * <ul> * <li>Users want to enable it.</li> * <li>There is no satellite communication restriction, which is added by - * {@link #addSatelliteAttachRestrictionForCarrier(int, int, Executor, Consumer)}</li> + * {@link #addAttachRestrictionForCarrier(int, int, Executor, Consumer)}</li> * <li>The carrier config {@link * android.telephony.CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to * {@code true}.</li> @@ -1749,17 +1751,17 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) - public void requestSatelliteAttachEnabledForCarrier(int subId, boolean enableSatellite, + public void requestAttachEnabledForCarrier(int subId, boolean enableSatellite, @NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { Objects.requireNonNull(executor); Objects.requireNonNull(resultListener); if (enableSatellite) { - removeSatelliteAttachRestrictionForCarrier(subId, + removeAttachRestrictionForCarrier(subId, SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener); } else { - addSatelliteAttachRestrictionForCarrier(subId, + addAttachRestrictionForCarrier(subId, SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener); } } @@ -1783,13 +1785,13 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) - public void requestIsSatelliteAttachEnabledForCarrier(int subId, + public void requestIsAttachEnabledForCarrier(int subId, @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); - Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier(subId); + Set<Integer> restrictionReason = getAttachRestrictionReasonsForCarrier(subId); executor.execute(() -> callback.onResult( !restrictionReason.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER))); } @@ -1808,7 +1810,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) - public void addSatelliteAttachRestrictionForCarrier(int subId, + public void addAttachRestrictionForCarrier(int subId, @SatelliteCommunicationRestrictionReason int reason, @NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { @@ -1826,13 +1828,13 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.addSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback); + telephony.addAttachRestrictionForCarrier(subId, reason, errorCallback); } else { executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("addSatelliteAttachRestrictionForCarrier() RemoteException:" + ex); + loge("addAttachRestrictionForCarrier() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } } @@ -1851,7 +1853,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) - public void removeSatelliteAttachRestrictionForCarrier(int subId, + public void removeAttachRestrictionForCarrier(int subId, @SatelliteCommunicationRestrictionReason int reason, @NonNull @CallbackExecutor Executor executor, @SatelliteResult @NonNull Consumer<Integer> resultListener) { @@ -1869,20 +1871,20 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.removeSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback); + telephony.removeAttachRestrictionForCarrier(subId, reason, errorCallback); } else { executor.execute(() -> Binder.withCleanCallingIdentity( () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE))); } } catch (RemoteException ex) { - loge("removeSatelliteAttachRestrictionForCarrier() RemoteException:" + ex); + loge("removeAttachRestrictionForCarrier() RemoteException:" + ex); ex.rethrowAsRuntimeException(); } } /** * Get reasons for disallowing satellite attach, as requested by - * {@link #addSatelliteAttachRestrictionForCarrier(int, int, Executor, Consumer)} + * {@link #addAttachRestrictionForCarrier(int, int, Executor, Consumer)} * * @param subId The subscription ID of the carrier. * @return Set of reasons for disallowing satellite communication. @@ -1894,7 +1896,7 @@ public final class SatelliteManager { @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @SatelliteCommunicationRestrictionReason @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) - @NonNull public Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier(int subId) { + @NonNull public Set<Integer> getAttachRestrictionReasonsForCarrier(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid subscription ID"); } @@ -1903,7 +1905,7 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { int[] receivedArray = - telephony.getSatelliteAttachRestrictionReasonsForCarrier(subId); + telephony.getAttachRestrictionReasonsForCarrier(subId); if (receivedArray.length == 0) { logd("receivedArray is empty, create empty set"); return new HashSet<>(); @@ -1914,7 +1916,7 @@ public final class SatelliteManager { throw new IllegalStateException("Telephony service is null."); } } catch (RemoteException ex) { - loge("getSatelliteAttachRestrictionReasonsForCarrier() RemoteException: " + ex); + loge("getAttachRestrictionReasonsForCarrier() RemoteException: " + ex); ex.rethrowAsRuntimeException(); } return new HashSet<>(); @@ -2090,7 +2092,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @SatelliteResult public int registerForSatelliteCapabilitiesChanged( + @SatelliteResult public int registerForCapabilitiesChanged( @NonNull @CallbackExecutor Executor executor, @NonNull SatelliteCapabilitiesCallback callback) { Objects.requireNonNull(executor); @@ -2110,12 +2112,12 @@ public final class SatelliteManager { } }; sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback); - return telephony.registerForSatelliteCapabilitiesChanged(mSubId, internalCallback); + return telephony.registerForCapabilitiesChanged(mSubId, internalCallback); } else { throw new IllegalStateException("Telephony service is null."); } } catch (RemoteException ex) { - loge("registerForSatelliteCapabilitiesChanged() RemoteException: " + ex); + loge("registerForCapabilitiesChanged() RemoteException: " + ex); ex.rethrowAsRuntimeException(); } return SATELLITE_RESULT_REQUEST_FAILED; @@ -2126,14 +2128,14 @@ public final class SatelliteManager { * If callback was not registered before, the request will be ignored. * * @param callback The callback that was passed to. - * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}. + * {@link #registerForCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}. * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - public void unregisterForSatelliteCapabilitiesChanged( + public void unregisterForCapabilitiesChanged( @NonNull SatelliteCapabilitiesCallback callback) { Objects.requireNonNull(callback); ISatelliteCapabilitiesCallback internalCallback = @@ -2143,15 +2145,15 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForSatelliteCapabilitiesChanged(mSubId, internalCallback); + telephony.unregisterForCapabilitiesChanged(mSubId, internalCallback); } else { - loge("unregisterForSatelliteCapabilitiesChanged: No internal callback."); + loge("unregisterForCapabilitiesChanged: No internal callback."); } } else { throw new IllegalStateException("Telephony service is null."); } } catch (RemoteException ex) { - loge("unregisterForSatelliteCapabilitiesChanged() RemoteException: " + ex); + loge("unregisterForCapabilitiesChanged() RemoteException: " + ex); ex.rethrowAsRuntimeException(); } } @@ -2166,7 +2168,7 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) - @NonNull public List<String> getAllSatellitePlmnsForCarrier(int subId) { + @NonNull public List<String> getSatellitePlmnsForCarrier(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid subscription ID"); } @@ -2174,12 +2176,12 @@ public final class SatelliteManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getAllSatellitePlmnsForCarrier(subId); + return telephony.getSatellitePlmnsForCarrier(subId); } else { throw new IllegalStateException("Telephony service is null."); } } catch (RemoteException ex) { - loge("getAllSatellitePlmnsForCarrier() RemoteException: " + ex); + loge("getSatellitePlmnsForCarrier() RemoteException: " + ex); ex.rethrowAsRuntimeException(); } return new ArrayList<>(); diff --git a/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl index b7712bd83cf6..655da740012b 100644 --- a/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl +++ b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl @@ -16,7 +16,7 @@ package android.telephony.satellite.stub; -import android.telephony.satellite.NtnSignalStrength; +import android.telephony.satellite.stub.NtnSignalStrength; /** * Consumer pattern for a request that receives the signal strength of non-terrestrial network from diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java index c0d0830e6105..abacd1527729 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java +++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java @@ -181,7 +181,7 @@ public class SatelliteImplBase extends SatelliteService { executeMethodAsync( () -> SatelliteImplBase.this .sendSatelliteDatagram(datagram, isEmergency, resultCallback), - "sendSatelliteDatagram"); + "sendDatagram"); } @Override @@ -201,7 +201,7 @@ public class SatelliteImplBase extends SatelliteService { () -> SatelliteImplBase.this .requestIsSatelliteCommunicationAllowedForCurrentLocation( resultCallback, callback), - "requestIsSatelliteCommunicationAllowedForCurrentLocation"); + "requestIsCommunicationAllowedForCurrentLocation"); } @Override diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 24296f4d9031..a1fc064d0d81 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2907,7 +2907,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback); + void unregisterForModemStateChanged(int subId, ISatelliteModemStateCallback callback); /** * Register to receive incoming datagrams over satellite. @@ -2919,18 +2919,18 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForSatelliteDatagram(int subId, ISatelliteDatagramCallback callback); + int registerForIncomingDatagram(int subId, ISatelliteDatagramCallback callback); /** * Unregister to stop receiving incoming datagrams over satellite. * If callback was not registered before, the request will be ignored. * * @param subId The subId of the subscription to unregister for incoming satellite datagrams. - * @param callback The callback that was passed to registerForSatelliteDatagram. + * @param callback The callback that was passed to registerForIncomingDatagram. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForSatelliteDatagram(int subId, ISatelliteDatagramCallback callback); + void unregisterForIncomingDatagram(int subId, ISatelliteDatagramCallback callback); /** * Poll pending satellite datagrams over satellite. @@ -2940,7 +2940,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void pollPendingSatelliteDatagrams(int subId, IIntegerConsumer callback); + void pollPendingDatagrams(int subId, IIntegerConsumer callback); /** * Send datagram over satellite. @@ -2954,9 +2954,8 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void sendSatelliteDatagram(int subId, int datagramType, - in SatelliteDatagram datagram, in boolean needFullScreenPointingUI, - IIntegerConsumer callback); + void sendDatagram(int subId, int datagramType, in SatelliteDatagram datagram, + in boolean needFullScreenPointingUI, IIntegerConsumer callback); /** * Request to get whether satellite communication is allowed for the current location. @@ -2968,8 +2967,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestIsSatelliteCommunicationAllowedForCurrentLocation(int subId, - in ResultReceiver receiver); + void requestIsCommunicationAllowedForCurrentLocation(int subId, in ResultReceiver receiver); /** * Request to get the time after which the satellite will be visible. @@ -3104,8 +3102,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void addSatelliteAttachRestrictionForCarrier(int subId, int reason, - in IIntegerConsumer callback); + void addAttachRestrictionForCarrier(int subId, int reason, in IIntegerConsumer callback); /** * Remove a restriction reason for disallowing satellite communication. @@ -3117,12 +3114,11 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void removeSatelliteAttachRestrictionForCarrier(int subId, int reason, - in IIntegerConsumer callback); + void removeAttachRestrictionForCarrier(int subId, int reason, in IIntegerConsumer callback); /** * Get reasons for disallowing satellite communication, as requested by - * {@link #addSatelliteAttachRestrictionForCarrier(int, int)}. + * {@link #addAttachRestrictionForCarrier(int, int)}. * * @param subId The subId of the subscription to request for. * @@ -3130,7 +3126,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int[] getSatelliteAttachRestrictionReasonsForCarrier(int subId); + int[] getAttachRestrictionReasonsForCarrier(int subId); /** * Request to get the signal strength of the satellite connection. @@ -3170,8 +3166,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForNtnSignalStrengthChanged(int subId, - in INtnSignalStrengthCallback callback); + void unregisterForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback); /** * Registers for satellite capabilities change event from the satellite service. @@ -3181,19 +3176,18 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForSatelliteCapabilitiesChanged(int subId, - in ISatelliteCapabilitiesCallback callback); + int registerForCapabilitiesChanged(int subId, in ISatelliteCapabilitiesCallback callback); /** * Unregisters for satellite capabilities change event from the satellite service. * If callback was not registered before, the request will be ignored. * * @param callback The callback that was passed to. - * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}. + * {@link #registerForCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForSatelliteCapabilitiesChanged(int subId, + void unregisterForCapabilitiesChanged(int subId, in ISatelliteCapabilitiesCallback callback); /** @@ -3253,7 +3247,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.MODIFY_PHONE_STATE)") - void setEnableNullCipherNotifications(boolean enable); + void setNullCipherNotificationsEnabled(boolean enable); /** * Get whether notifications are enabled for null cipher or integrity algorithms in use by the @@ -3280,5 +3274,5 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - List<String> getAllSatellitePlmnsForCarrier(int subId); + List<String> getSatellitePlmnsForCarrier(int subId); } diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java index 5aaf30a5b3a7..14230fe4c323 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java @@ -134,7 +134,7 @@ public class EmbeddedWindowService extends Service { c.drawText("Remote", 250, 250, paint); surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - mInputToken = wm.registerBatchedSurfaceControlInputReceiver(displayId, hostToken, + wm.registerBatchedSurfaceControlInputReceiver(displayId, hostToken, mSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-remote " + event); @@ -147,11 +147,9 @@ public class EmbeddedWindowService extends Service { @Override public void tearDownEmbeddedSurfaceControl() { if (mSurfaceControl != null) { - new SurfaceControl.Transaction().remove(mSurfaceControl); - } - if (mInputToken != null) { WindowManager wm = getSystemService(WindowManager.class); - wm.unregisterSurfaceControlInputReceiver(mInputToken); + wm.unregisterSurfaceControlInputReceiver(mSurfaceControl); + new SurfaceControl.Transaction().remove(mSurfaceControl).apply(); } } } diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java index e5f8f47aeecd..7330ec14011b 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java @@ -50,10 +50,11 @@ public class SurfaceInputTestActivity extends Activity { private static final String TAG = "SurfaceInputTestActivity"; private SurfaceView mLocalSurfaceView; private SurfaceView mRemoteSurfaceView; - private IBinder mInputToken; private IAttachEmbeddedWindow mIAttachEmbeddedWindow; private SurfaceControl mParentSurfaceControl; + private SurfaceControl mLocalSurfaceControl; + private final ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { @@ -112,30 +113,33 @@ public class SurfaceInputTestActivity extends Activity { @Override protected void onDestroy() { super.onDestroy(); - getWindowManager().unregisterSurfaceControlInputReceiver(mInputToken); + if (mLocalSurfaceControl != null) { + getWindowManager().unregisterSurfaceControlInputReceiver(mLocalSurfaceControl); + new SurfaceControl.Transaction().remove(mLocalSurfaceControl).apply(); + } } private void addLocalChildSurfaceControl(AttachedSurfaceControl attachedSurfaceControl) { - SurfaceControl surfaceControl = new SurfaceControl.Builder().setName("LocalSC") + mLocalSurfaceControl = new SurfaceControl.Builder().setName("LocalSC") .setBufferSize(100, 100).build(); - attachedSurfaceControl.buildReparentTransaction(surfaceControl) - .setVisibility(surfaceControl, true) - .setCrop(surfaceControl, new Rect(0, 0, 100, 100)) - .setPosition(surfaceControl, 250, 1000) - .setLayer(surfaceControl, 1).apply(); + attachedSurfaceControl.buildReparentTransaction(mLocalSurfaceControl) + .setVisibility(mLocalSurfaceControl, true) + .setCrop(mLocalSurfaceControl, new Rect(0, 0, 100, 100)) + .setPosition(mLocalSurfaceControl, 250, 1000) + .setLayer(mLocalSurfaceControl, 1).apply(); Paint paint = new Paint(); paint.setColor(Color.WHITE); paint.setTextSize(20); - Surface surface = new Surface(surfaceControl); + Surface surface = new Surface(mLocalSurfaceControl); Canvas c = surface.lockCanvas(null); c.drawColor(Color.GREEN); c.drawText("Local SC", 0, 0, paint); surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - mInputToken = wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), - attachedSurfaceControl.getHostToken(), surfaceControl, + wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + attachedSurfaceControl.getHostToken(), mLocalSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-sc " + event); return false; @@ -143,8 +147,6 @@ public class SurfaceInputTestActivity extends Activity { } private final SurfaceHolder.Callback mLocalSurfaceViewCallback = new SurfaceHolder.Callback() { - private IBinder mInputToken; - @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { Paint paint = new Paint(); @@ -157,7 +159,7 @@ public class SurfaceInputTestActivity extends Activity { holder.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - mInputToken = wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), mLocalSurfaceView.getHostToken(), mLocalSurfaceView.getSurfaceControl(), Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-local " + event); @@ -173,9 +175,8 @@ public class SurfaceInputTestActivity extends Activity { @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { - if (mInputToken != null) { - getWindowManager().unregisterSurfaceControlInputReceiver(mInputToken); - } + getWindowManager().unregisterSurfaceControlInputReceiver( + mLocalSurfaceView.getSurfaceControl()); } }; diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index f3f183815d0b..642a5618b6ad 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -1988,6 +1988,8 @@ class Linker { context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); context_->SetSplitNameDependencies(app_info_.split_name_dependencies); + std::unique_ptr<xml::XmlResource> pre_flags_filter_manifest_xml = manifest_xml->Clone(); + FeatureFlagsFilterOptions flags_filter_options; if (context_->GetMinSdkVersion() > SDK_UPSIDE_DOWN_CAKE) { // For API version > U, PackageManager will dynamically read the flag values and disable @@ -2297,7 +2299,12 @@ class Linker { } if (options_.generate_java_class_path) { - if (!WriteManifestJavaFile(manifest_xml.get())) { + // The FeatureFlagsFilter may remove <permission> and <permission-group> elements that + // generate constants in the Manifest Java file. While we want those permissions and + // permission groups removed in the SDK (i.e., if a feature flag is disabled), the + // constants should still remain so that code referencing it (e.g., within a feature + // flag check) will still compile. Therefore we use the manifest XML before the filter. + if (!WriteManifestJavaFile(pre_flags_filter_manifest_xml.get())) { error = true; } } diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp index 9323f3b95eac..6cc42f17c0a1 100644 --- a/tools/aapt2/cmd/Link_test.cpp +++ b/tools/aapt2/cmd/Link_test.cpp @@ -1021,9 +1021,11 @@ TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) { .AddContents(manifest_contents) .Build(); + const std::string app_java = GetTestPath("app-java"); auto app_link_args = LinkCommandBuilder(this) .SetManifestFile(app_manifest) .AddParameter("-I", android_apk) + .AddParameter("--java", app_java) .AddParameter("--feature-flags", "flag=false"); const std::string app_apk = GetTestPath("app.apk"); @@ -1038,6 +1040,12 @@ TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) { ASSERT_THAT(root, NotNull()); auto maybe_removed = root->FindChild({}, "permission"); ASSERT_THAT(maybe_removed, IsNull()); + + // Code for the permission should be generated even if the element is removed + const std::string manifest_java = app_java + "/com/example/app/Manifest.java"; + std::string manifest_java_contents; + ASSERT_TRUE(android::base::ReadFileToString(manifest_java, &manifest_java_contents)); + EXPECT_THAT(manifest_java_contents, HasSubstr(" public static final String FOO=\"FOO\";")); } TEST_F(LinkTest, FeatureFlagEnabled_SdkAtMostUDC) { diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 0b16e2c7efe4..d03f97e28156 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -415,6 +415,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, android::IDiagn intent_filter_action["action"].Action(RequiredNameIsNotEmpty); intent_filter_action["category"].Action(RequiredNameIsNotEmpty); intent_filter_action["data"]; + intent_filter_action["uri-relative-filter-group"]; + intent_filter_action["uri-relative-filter-group"]["data"]; // Common <meta-data> actions. xml::XmlNodeAction meta_data_action; diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java index 2e47d48f4fa0..65da4a144160 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java @@ -28,6 +28,7 @@ public class MessageQueue_host { private final Object mPoller = new Object(); private volatile boolean mPolling; + private volatile boolean mPendingWake; private void validate() { if (mDeleted) { @@ -62,7 +63,9 @@ public class MessageQueue_host { synchronized (q.mPoller) { q.mPolling = true; try { - if (timeoutMillis == 0) { + if (q.mPendingWake) { + // Calling with pending wake returns immediately + } else if (timeoutMillis == 0) { // Calling epoll_wait() with 0 returns immediately } else if (timeoutMillis == -1) { q.mPoller.wait(); @@ -72,6 +75,8 @@ public class MessageQueue_host { } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + // Any reason for returning counts as a "wake", so clear pending + q.mPendingWake = false; q.mPolling = false; } } @@ -79,6 +84,7 @@ public class MessageQueue_host { public static void nativeWake(long ptr) { var q = getInstance(ptr); synchronized (q.mPoller) { + q.mPendingWake = true; q.mPoller.notifyAll(); } } |